From 2dd5ae89420743baa331e2a89f3629be8443f2fa Mon Sep 17 00:00:00 2001 From: Pradnya Khalate Date: Thu, 23 Jan 2025 11:45:31 -0800 Subject: [PATCH 1/4] * Skeleton for output record logging and parsing -- primitive types TODO: Handle arrays (vector of results) * Naive way of parsing integer array log Signed-off-by: Pradnya Khalate --- runtime/common/RecordLogger.h | 69 +++++++++++++++ runtime/common/RecordParser.h | 85 +++++++++++++++++++ unittests/CMakeLists.txt | 1 + unittests/output_record/CMakeLists.txt | 21 +++++ .../output_record/RecordLoggerTester.cpp | 24 ++++++ .../output_record/RecordParserTester.cpp | 48 +++++++++++ 6 files changed, 248 insertions(+) create mode 100644 runtime/common/RecordLogger.h create mode 100644 runtime/common/RecordParser.h create mode 100644 unittests/output_record/CMakeLists.txt create mode 100644 unittests/output_record/RecordLoggerTester.cpp create mode 100644 unittests/output_record/RecordParserTester.cpp diff --git a/runtime/common/RecordLogger.h b/runtime/common/RecordLogger.h new file mode 100644 index 0000000000..0d063751ed --- /dev/null +++ b/runtime/common/RecordLogger.h @@ -0,0 +1,69 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2025 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 +#include +#include +#include +#include + +namespace cudaq { + +struct RecordLogger { +private: + bool emitLabel = false; + std::stringstream ss; + +public: + void emitHeader() { + ss << "HEADER\tschema_name\t" << (emitLabel ? "labeled" : "ordered") + << "\n"; + ss << "HEADER\tschema_version\t1.0\n"; + } + + template + void logSingleRecord(const T &record, std::string label = "") { + ss << "OUTPUT\t"; + if (typeid(T) == typeid(bool)) + ss << "BOOL\t" << (record ? "true" : "false"); + else if (typeid(T) == typeid(int)) + ss << "INT\t" << record; + else if (typeid(T) == typeid(float)) + ss << "FLOAT\t" << record; + else if (typeid(T) == typeid(double)) + ss << "DOUBLE\t" << record; + else + throw std::runtime_error("Unsupported output record type"); + if (emitLabel) { + if (label.empty()) + throw std::runtime_error( + "Non-empty label expected for the output record"); + else + ss << "\t" << label; + } + ss << "\n"; + } + + template + void log(const std::vector &records) { + emitHeader(); + for (auto r : records) { + ss << "START\n"; + logSingleRecord(r); + ss << "END\t0\n"; // Assumes success always + } + } + + std::string getLog() { return ss.str(); } + + void writeToFile(std::ofstream &outFile) { outFile << ss.rdbuf(); } +}; + +} // namespace cudaq diff --git a/runtime/common/RecordParser.h b/runtime/common/RecordParser.h new file mode 100644 index 0000000000..b784b5b6fb --- /dev/null +++ b/runtime/common/RecordParser.h @@ -0,0 +1,85 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2025 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/utils/cudaq_utils.h" +#include +#include + +namespace cudaq { + +struct OutputRecord { + void *buffer; + std::size_t size; +}; + +struct RecordParser { +private: + bool labelExpected = false; + +public: + std::vector parse(const std::string &data) { + std::vector results; + std::vector lines = cudaq::split(data, '\n'); + std::size_t arrSize = 0; + int arrIdx = -1; + for (auto line : lines) { + std::vector entries = cudaq::split(line, '\t'); + if (entries.empty()) + continue; + if (entries[0] != "OUTPUT") + throw std::runtime_error("Invalid data"); + + /// TODO: Handle labeled records + if ("BOOL" == entries[1]) { + bool value; + if ("true" == entries[2]) + value = true; + else if ("false" == entries[2]) + value = false; + results.emplace_back( + OutputRecord{static_cast(new bool(value)), sizeof(bool)}); + } else if ("INT" == entries[1]) { + if (0 != arrSize) { + if (0 == arrIdx) { + int *resArr = new int[arrSize]; + results.emplace_back(OutputRecord{static_cast(resArr), + sizeof(int) * arrSize}); + } + static_cast(results.back().buffer)[arrIdx++] = + std::stoi(entries[2]); + if (arrIdx == arrSize) { + arrSize = 0; + arrIdx = -1; + } + } else + results.emplace_back( + OutputRecord{static_cast(new int(std::stoi(entries[2]))), + sizeof(int)}); + } else if ("FLOAT" == entries[1]) { + results.emplace_back( + OutputRecord{static_cast(new int(std::stof(entries[2]))), + sizeof(float)}); + } else if ("DOUBLE" == entries[1]) { + results.emplace_back( + OutputRecord{static_cast(new int(std::stod(entries[2]))), + sizeof(double)}); + } else if ("ARRAY" == entries[1]) { + arrSize = std::stoi(entries[2]); + if (0 == arrSize) + throw std::runtime_error("Got empty array"); + arrIdx = 0; + } + /// TODO: Handle more types + } + return results; + } +}; + +} // namespace cudaq diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index 81b7202ae6..b41daa775e 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -330,6 +330,7 @@ endif() add_subdirectory(backends) add_subdirectory(Optimizer) +add_subdirectory(output_record) set(CUDAQ_BRAKET_RUNTIME_TEST_SOURCES # Integration tests diff --git a/unittests/output_record/CMakeLists.txt b/unittests/output_record/CMakeLists.txt new file mode 100644 index 0000000000..979171b027 --- /dev/null +++ b/unittests/output_record/CMakeLists.txt @@ -0,0 +1,21 @@ +# ============================================================================ # +# Copyright (c) 2022 - 2025 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. # +# ============================================================================ # + +add_executable(test_record RecordLoggerTester.cpp RecordParserTester.cpp) + +if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT APPLE) + target_link_options(test_record PRIVATE -Wl,--no-as-needed) +endif() +target_include_directories(test_record PRIVATE ..) +target_link_libraries(test_record PRIVATE + fmt::fmt-header-only + cudaq-common + gtest_main +) + +gtest_discover_tests(test_record) diff --git a/unittests/output_record/RecordLoggerTester.cpp b/unittests/output_record/RecordLoggerTester.cpp new file mode 100644 index 0000000000..f0ef198b0a --- /dev/null +++ b/unittests/output_record/RecordLoggerTester.cpp @@ -0,0 +1,24 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 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 "CUDAQTestUtils.h" +#include "common/RecordLogger.h" +#include + +CUDAQ_TEST(LoggerTester, checkBoolean) { + std::vector results = {true, false, false, true}; + + const std::string expectedLog = + "HEADER\tschema_name\tordered\nHEADER\tschema_version\t1.0\n" + "START\nOUTPUT\tBOOL\ttrue\nEND\t0\nSTART\nOUTPUT\tBOOL\tfalse\nEND\t0\n" + "START\nOUTPUT\tBOOL\tfalse\nEND\t0\nSTART\nOUTPUT\tBOOL\ttrue\nEND\t0\n"; + + cudaq::RecordLogger logger; + logger.log(results); + EXPECT_TRUE(expectedLog == logger.getLog()); +} \ No newline at end of file diff --git a/unittests/output_record/RecordParserTester.cpp b/unittests/output_record/RecordParserTester.cpp new file mode 100644 index 0000000000..e7597427e4 --- /dev/null +++ b/unittests/output_record/RecordParserTester.cpp @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 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 "CUDAQTestUtils.h" +#include "common/RecordParser.h" +#include + +CUDAQ_TEST(ParserTester, checkSingleBoolean) { + const std::string log = "OUTPUT\tBOOL\ttrue"; + cudaq::RecordParser parser; + auto results = parser.parse(log); + EXPECT_EQ(1, results.size()); + EXPECT_EQ(true, *static_cast(results[0].buffer)); +} + +CUDAQ_TEST(ParserTester, checkIntegers) { + const std::string log = "OUTPUT\tINT\t0\n" + "OUTPUT\tINT\t1\n" + "OUTPUT\tINT\t2\n"; + cudaq::RecordParser parser; + auto results = parser.parse(log); + EXPECT_EQ(3, results.size()); + EXPECT_EQ(2, *static_cast(results[2].buffer)); +} + +CUDAQ_TEST(ParserTester, checkArray) { + const std::string log = "OUTPUT\tARRAY\t4\n" + "OUTPUT\tINT\t0\n" + "OUTPUT\tINT\t1\n" + "OUTPUT\tINT\t1\n" + "OUTPUT\tINT\t0"; + cudaq::RecordParser parser; + auto results = parser.parse(log); + EXPECT_EQ(1, results.size()); + auto count = results[0].size / sizeof(int); + EXPECT_EQ(4, count); + auto *ptr = static_cast(results[0].buffer); + std::vector got(count); + for (int i = 0; i < count; ++i) + got[i] = ptr[i]; + EXPECT_EQ(1, got[2]); + EXPECT_EQ(0, got[3]); +} \ No newline at end of file From cf3eb4fc14550ac5a6cb61fb63a31ca6733b04ed Mon Sep 17 00:00:00 2001 From: Pradnya Khalate Date: Sun, 2 Feb 2025 00:56:55 -0800 Subject: [PATCH 2/4] * More tests - labeled arrays, multiple shots, failing test for tuple Signed-off-by: Pradnya Khalate --- runtime/common/RecordParser.h | 285 +++++++++++++++--- .../output_record/RecordParserTester.cpp | 73 ++++- 2 files changed, 311 insertions(+), 47 deletions(-) diff --git a/runtime/common/RecordParser.h b/runtime/common/RecordParser.h index b784b5b6fb..4904e65068 100644 --- a/runtime/common/RecordParser.h +++ b/runtime/common/RecordParser.h @@ -12,72 +12,267 @@ #include #include +/// REVISIT: Memory management! namespace cudaq { +// QIR output schema +enum struct SchemaType { LABELED, ORDERED }; +enum struct RecordType { HEADER, METADATA, OUTPUT, START, END }; +enum struct OutputType { RESULT, BOOL, INT, DOUBLE }; +enum struct ContainerType { ARRAY, TUPLE }; + +// A container for the results from a single shot of executing a kernel which +// returns data of type `T`. The return value is stored in memory buffer +// starting with `buffer` and size in bytes in `size`. +/// TODO: Look at `RunResultSpan` type struct OutputRecord { void *buffer; std::size_t size; }; +// Helper to keep track of the state of the parser logic. +struct ContainerIterator { + ContainerType cTy; + std::size_t cSize = 0; + std::size_t cIndex = 0; + std::size_t remaining = 0; + std::vector types; + + void initialize(ContainerType c) { + cTy = c; + cSize = 0; + cIndex = 0; + remaining = 0; + types = {}; + } + + bool isFull() { + if ((cSize > 0) && (remaining == 0)) + return true; + return false; + } +}; + +// A generic parser for QIR output record logs. struct RecordParser { private: - bool labelExpected = false; + SchemaType schema = SchemaType::ORDERED; + RecordType currentRecord; + OutputType currentOutput; + bool isInContainer = false; + ContainerIterator containerIt; + + template + void addPrimitiveRecord(T value) { + results.emplace_back( + OutputRecord{static_cast(new T(value)), sizeof(T)}); + } + + template + void addArrayRecord(size_t arrSize) { + T *resArr = new T[arrSize]; + results.emplace_back( + OutputRecord{static_cast(resArr), sizeof(T) * arrSize}); + } + + template + void addToArray(T value) { + if (SchemaType::ORDERED == schema) { + if (0 == containerIt.cIndex) { + addArrayRecord(containerIt.cSize); + containerIt.remaining = containerIt.cSize; + } + static_cast(results.back().buffer)[containerIt.cIndex++] = value; + } else if (SchemaType::LABELED == schema) { + static_cast(results.back().buffer)[containerIt.cIndex] = value; + } + containerIt.remaining--; + if (containerIt.isFull()) + isInContainer = false; + } + + OutputType extractPrimitiveType(const std::string &label) { + if ('i' == label[0]) { + auto digits = std::stoi(label.substr(1)); + if (1 == digits) + return OutputType::BOOL; + return OutputType::INT; + } else if ('f' == label[0]) + return OutputType::DOUBLE; + throw std::runtime_error("Unknown datatype in label"); + } + + std::size_t extractContainerIndex(const std::string &label) { + if (('[' == label[0]) && (']' == label[label.size() - 1])) + return std::stoi(label.substr(1, label.size() - 1)); + if ('.' == label[0]) + return std::stoi(label.substr(1, label.size() - 1)); + throw std::runtime_error("Index not found in label"); + } + + /// Parse string like "array<3 x i32>" + std::pair + extractArrayInfo(const std::string &label) { + auto isArray = label.find("array"); + auto lessThan = label.find('<'); + auto greaterThan = label.find('>'); + auto x = label.find('x'); + if ((isArray == std::string::npos) || (lessThan == std::string::npos) || + (greaterThan == std::string::npos) || (x == std::string::npos)) + throw std::runtime_error("Array label missing keyword"); + std::size_t arrSize = + std::stoi(label.substr(lessThan + 1, x - lessThan - 2)); + OutputType arrType = + extractPrimitiveType(label.substr(x + 2, greaterThan - x - 2)); + return std::make_pair(arrSize, arrType); + } + + void prcoessSingleRecord(const std::string &recValue, + const std::string &recLabel) { + if (!recLabel.empty()) { + if (isInContainer) + containerIt.cIndex = extractContainerIndex(recLabel); + else if (extractPrimitiveType(recLabel) != currentOutput) + throw std::runtime_error("Type mismatch in label"); + } + switch (currentOutput) { + case OutputType::BOOL: { + bool value; + if ("true" == recValue) + value = true; + else if ("false" == recValue) + value = false; + else + throw std::runtime_error("Invalid boolean value"); + if (isInContainer) + addToArray(value); + else + addPrimitiveRecord(value); + break; + } + case OutputType::INT: { + if (isInContainer) + addToArray(std::stoi(recValue)); + else + addPrimitiveRecord(std::stoi(recValue)); + break; + } + case OutputType::DOUBLE: { + if (isInContainer) + addToArray(std::stod(recValue)); + else + addPrimitiveRecord(std::stod(recValue)); + break; + } + default: + throw std::runtime_error("Unsupported output type"); + } + } public: + std::vector results; + std::vector parse(const std::string &data) { - std::vector results; std::vector lines = cudaq::split(data, '\n'); - std::size_t arrSize = 0; - int arrIdx = -1; + if (lines.empty()) + return {}; + for (auto line : lines) { std::vector entries = cudaq::split(line, '\t'); if (entries.empty()) continue; - if (entries[0] != "OUTPUT") + + if ("HEADER" == entries[0]) + currentRecord = RecordType::HEADER; + else if ("METADATA" == entries[0]) + currentRecord = RecordType::METADATA; + else if ("OUTPUT" == entries[0]) + currentRecord = RecordType::OUTPUT; + else if ("START" == entries[0]) + currentRecord = RecordType::START; + else if ("END" == entries[0]) + currentRecord = RecordType::END; + else throw std::runtime_error("Invalid data"); - /// TODO: Handle labeled records - if ("BOOL" == entries[1]) { - bool value; - if ("true" == entries[2]) - value = true; - else if ("false" == entries[2]) - value = false; - results.emplace_back( - OutputRecord{static_cast(new bool(value)), sizeof(bool)}); - } else if ("INT" == entries[1]) { - if (0 != arrSize) { - if (0 == arrIdx) { - int *resArr = new int[arrSize]; - results.emplace_back(OutputRecord{static_cast(resArr), - sizeof(int) * arrSize}); - } - static_cast(results.back().buffer)[arrIdx++] = - std::stoi(entries[2]); - if (arrIdx == arrSize) { - arrSize = 0; - arrIdx = -1; + switch (currentRecord) { + case RecordType::HEADER: { + if ("schema_name" == entries[1]) { + if ("labeled" == entries[2]) + schema = SchemaType::LABELED; + else if ("ordered" == entries[2]) + schema = SchemaType::ORDERED; + else + throw std::runtime_error("Unknown schema type"); + } + /// TODO: Check schema version + break; + } + case RecordType::METADATA: + // ignore metadata for now + break; + case RecordType::START: + // indicates start of a shot + break; + case RecordType::END: { + // indicates end of a shot + if (entries.size() < 2) + throw std::runtime_error("Missing shot status"); + if ("0" != entries[1]) + throw std::runtime_error("Cannot handle unsuccessful shot"); + break; + } + case RecordType::OUTPUT: { + if (entries.size() < 3) + throw std::runtime_error("Insufficent data in a record"); + if ((schema == SchemaType::LABELED) && (entries.size() != 4)) + throw std::runtime_error( + "Unexpected record size for a labeled record"); + + std::string recType = entries[1]; + std::string recValue = entries[2]; + std::string recLabel = (entries.size() == 4) ? entries[3] : ""; + + if ("RESULT" == recType) + throw std::runtime_error("This type is not yet supported"); + if ("TUPLE" == recType) + throw std::runtime_error("This type is not yet supported"); + + if ("ARRAY" == recType) { + isInContainer = true; + containerIt.initialize(ContainerType::ARRAY); + containerIt.cSize = std::stoi(recValue); + if (0 == containerIt.cSize) + throw std::runtime_error("Got empty array"); + if (SchemaType::LABELED == schema) { + auto info = extractArrayInfo(recLabel); + if (info.first != containerIt.cSize) + throw std::runtime_error("Array size mismatch in label"); + containerIt.remaining = containerIt.cSize; + if (OutputType::BOOL == info.second) + addArrayRecord(info.first); + else if (OutputType::INT == info.second) + addArrayRecord(info.first); + else if (OutputType::DOUBLE == info.second) + addArrayRecord(info.first); } - } else - results.emplace_back( - OutputRecord{static_cast(new int(std::stoi(entries[2]))), - sizeof(int)}); - } else if ("FLOAT" == entries[1]) { - results.emplace_back( - OutputRecord{static_cast(new int(std::stof(entries[2]))), - sizeof(float)}); - } else if ("DOUBLE" == entries[1]) { - results.emplace_back( - OutputRecord{static_cast(new int(std::stod(entries[2]))), - sizeof(double)}); - } else if ("ARRAY" == entries[1]) { - arrSize = std::stoi(entries[2]); - if (0 == arrSize) - throw std::runtime_error("Got empty array"); - arrIdx = 0; + } else { + if ("BOOL" == recType) + currentOutput = OutputType::BOOL; + else if ("INT" == recType) + currentOutput = OutputType::INT; + else if ("DOUBLE" == recType) + currentOutput = OutputType::DOUBLE; + else + throw std::runtime_error("Invalid data"); + prcoessSingleRecord(recValue, recLabel); + } + break; } - /// TODO: Handle more types - } + default: + throw std::runtime_error("Unknown record type"); + } + } // for line return results; } }; diff --git a/unittests/output_record/RecordParserTester.cpp b/unittests/output_record/RecordParserTester.cpp index e7597427e4..8cebe3ad09 100644 --- a/unittests/output_record/RecordParserTester.cpp +++ b/unittests/output_record/RecordParserTester.cpp @@ -28,7 +28,7 @@ CUDAQ_TEST(ParserTester, checkIntegers) { EXPECT_EQ(2, *static_cast(results[2].buffer)); } -CUDAQ_TEST(ParserTester, checkArray) { +CUDAQ_TEST(ParserTester, checkArrayOrdered) { const std::string log = "OUTPUT\tARRAY\t4\n" "OUTPUT\tINT\t0\n" "OUTPUT\tINT\t1\n" @@ -45,4 +45,73 @@ CUDAQ_TEST(ParserTester, checkArray) { got[i] = ptr[i]; EXPECT_EQ(1, got[2]); EXPECT_EQ(0, got[3]); -} \ No newline at end of file +} + +CUDAQ_TEST(ParserTester, checkArrayLabeled) { + const std::string log = "HEADER\tschema_name\tlabeled\n" + "HEADER\tschema_version\t1.0\n" + "START\n" + "OUTPUT\tARRAY\t3\tarray<3 x i32>\n" + "OUTPUT\tINT\t13\t[0]\n" + "OUTPUT\tINT\t42\t[1]\n" + "OUTPUT\tINT\t61\t[2]\n" + "END\t0"; + cudaq::RecordParser parser; + auto results = parser.parse(log); + EXPECT_EQ(1, results.size()); + auto count = results[0].size / sizeof(int); + EXPECT_EQ(3, count); + auto *ptr = static_cast(results[0].buffer); + std::vector got(count); + for (int i = 0; i < count; ++i) + got[i] = ptr[i]; + EXPECT_EQ(13, got[0]); + EXPECT_EQ(61, got[2]); +} + +CUDAQ_TEST(ParserTester, checkMultipleShots) { + const std::string log = "HEADER\tschema_name\tlabeled\n" + "START\n" + "OUTPUT\tARRAY\t2\tarray<2 x i1>\n" + "OUTPUT\tBOOL\ttrue\t[0]\n" + "OUTPUT\tBOOL\ttrue\t[1]\n" + "END\t0\n" + "START\n" + "OUTPUT\tARRAY\t2\tarray<2 x i1>\n" + "OUTPUT\tBOOL\tfalse\t[1]\n" + "OUTPUT\tBOOL\ttrue\t[0]\n" + "END\t0\n" + "START\n" + "OUTPUT\tARRAY\t2\tarray<2 x i1>\n" + "OUTPUT\tBOOL\ttrue\t[0]\n" + "OUTPUT\tBOOL\tfalse\t[1]\n" + "END\t0"; + cudaq::RecordParser parser; + auto results = parser.parse(log); + EXPECT_EQ(3, results.size()); + auto count = results[1].size / sizeof(bool); + EXPECT_EQ(2, count); + auto *ptr = static_cast(results[1].buffer); + std::vector got(count); + for (int i = 0; i < count; ++i) + got[i] = ptr[i]; + EXPECT_EQ(true, got[0]); + EXPECT_EQ(false, got[1]); +} + +CUDAQ_TEST(ParserTester, checkTuple) { + const std::string log = "HEADER\tschema_name\tlabeled\n" + "START\n" + "OUTPUT\tTUPLE\t3\ttuple\n" + "OUTPUT\tBOOL\ttrue\t.0\n" + "OUTPUT\tINT\t37\t.1\n" + "OUTPUT\tDOUBLE\t3.1416\t.2\n" + "END\t0"; + cudaq::RecordParser parser; + try { + parser.parse(log); + FAIL(); + } catch (std::exception &ex) { + EXPECT_STREQ("This type is not yet supported", ex.what()); + } +} From 22b2b7e4fcc354ad53d6baaafe2f731d935c8b4d Mon Sep 17 00:00:00 2001 From: Pradnya Khalate Date: Mon, 3 Feb 2025 22:43:58 -0800 Subject: [PATCH 3/4] * Clean-up: Removed the logger functionality since the API was implemented for testing only * Addressing PR comment Signed-off-by: Pradnya Khalate --- runtime/common/RecordLogger.h | 69 ------------------- runtime/common/RecordParser.h | 8 +-- unittests/output_record/CMakeLists.txt | 2 +- .../output_record/RecordLoggerTester.cpp | 24 ------- 4 files changed, 2 insertions(+), 101 deletions(-) delete mode 100644 runtime/common/RecordLogger.h delete mode 100644 unittests/output_record/RecordLoggerTester.cpp diff --git a/runtime/common/RecordLogger.h b/runtime/common/RecordLogger.h deleted file mode 100644 index 0d063751ed..0000000000 --- a/runtime/common/RecordLogger.h +++ /dev/null @@ -1,69 +0,0 @@ -/****************************************************************-*- C++ -*-**** - * Copyright (c) 2022 - 2025 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 -#include -#include -#include -#include - -namespace cudaq { - -struct RecordLogger { -private: - bool emitLabel = false; - std::stringstream ss; - -public: - void emitHeader() { - ss << "HEADER\tschema_name\t" << (emitLabel ? "labeled" : "ordered") - << "\n"; - ss << "HEADER\tschema_version\t1.0\n"; - } - - template - void logSingleRecord(const T &record, std::string label = "") { - ss << "OUTPUT\t"; - if (typeid(T) == typeid(bool)) - ss << "BOOL\t" << (record ? "true" : "false"); - else if (typeid(T) == typeid(int)) - ss << "INT\t" << record; - else if (typeid(T) == typeid(float)) - ss << "FLOAT\t" << record; - else if (typeid(T) == typeid(double)) - ss << "DOUBLE\t" << record; - else - throw std::runtime_error("Unsupported output record type"); - if (emitLabel) { - if (label.empty()) - throw std::runtime_error( - "Non-empty label expected for the output record"); - else - ss << "\t" << label; - } - ss << "\n"; - } - - template - void log(const std::vector &records) { - emitHeader(); - for (auto r : records) { - ss << "START\n"; - logSingleRecord(r); - ss << "END\t0\n"; // Assumes success always - } - } - - std::string getLog() { return ss.str(); } - - void writeToFile(std::ofstream &outFile) { outFile << ss.rdbuf(); } -}; - -} // namespace cudaq diff --git a/runtime/common/RecordParser.h b/runtime/common/RecordParser.h index 4904e65068..6b75c97414 100644 --- a/runtime/common/RecordParser.h +++ b/runtime/common/RecordParser.h @@ -36,21 +36,15 @@ struct ContainerIterator { std::size_t cSize = 0; std::size_t cIndex = 0; std::size_t remaining = 0; - std::vector types; void initialize(ContainerType c) { cTy = c; cSize = 0; cIndex = 0; remaining = 0; - types = {}; } - bool isFull() { - if ((cSize > 0) && (remaining == 0)) - return true; - return false; - } + bool isFull() { return ((cSize > 0) && (remaining == 0)); } }; // A generic parser for QIR output record logs. diff --git a/unittests/output_record/CMakeLists.txt b/unittests/output_record/CMakeLists.txt index 979171b027..e45593e53b 100644 --- a/unittests/output_record/CMakeLists.txt +++ b/unittests/output_record/CMakeLists.txt @@ -6,7 +6,7 @@ # the terms of the Apache License 2.0 which accompanies this distribution. # # ============================================================================ # -add_executable(test_record RecordLoggerTester.cpp RecordParserTester.cpp) +add_executable(test_record RecordParserTester.cpp) if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT APPLE) target_link_options(test_record PRIVATE -Wl,--no-as-needed) diff --git a/unittests/output_record/RecordLoggerTester.cpp b/unittests/output_record/RecordLoggerTester.cpp deleted file mode 100644 index f0ef198b0a..0000000000 --- a/unittests/output_record/RecordLoggerTester.cpp +++ /dev/null @@ -1,24 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2022 - 2025 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 "CUDAQTestUtils.h" -#include "common/RecordLogger.h" -#include - -CUDAQ_TEST(LoggerTester, checkBoolean) { - std::vector results = {true, false, false, true}; - - const std::string expectedLog = - "HEADER\tschema_name\tordered\nHEADER\tschema_version\t1.0\n" - "START\nOUTPUT\tBOOL\ttrue\nEND\t0\nSTART\nOUTPUT\tBOOL\tfalse\nEND\t0\n" - "START\nOUTPUT\tBOOL\tfalse\nEND\t0\nSTART\nOUTPUT\tBOOL\ttrue\nEND\t0\n"; - - cudaq::RecordLogger logger; - logger.log(results); - EXPECT_TRUE(expectedLog == logger.getLog()); -} \ No newline at end of file From 611c67343122e5881e025e4bf106bc38eb821aa0 Mon Sep 17 00:00:00 2001 From: Pradnya Khalate Date: Thu, 6 Feb 2025 11:36:23 -0800 Subject: [PATCH 4/4] * Fix the format of array label - Signed-off-by: Pradnya Khalate --- runtime/common/RecordParser.h | 7 +++---- unittests/output_record/RecordParserTester.cpp | 8 ++++---- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/runtime/common/RecordParser.h b/runtime/common/RecordParser.h index 6b75c97414..41f2eeee5a 100644 --- a/runtime/common/RecordParser.h +++ b/runtime/common/RecordParser.h @@ -104,7 +104,7 @@ struct RecordParser { throw std::runtime_error("Index not found in label"); } - /// Parse string like "array<3 x i32>" + /// Parse string like "array" std::pair extractArrayInfo(const std::string &label) { auto isArray = label.find("array"); @@ -114,10 +114,9 @@ struct RecordParser { if ((isArray == std::string::npos) || (lessThan == std::string::npos) || (greaterThan == std::string::npos) || (x == std::string::npos)) throw std::runtime_error("Array label missing keyword"); - std::size_t arrSize = - std::stoi(label.substr(lessThan + 1, x - lessThan - 2)); + std::size_t arrSize = std::stoi(label.substr(x + 2, greaterThan - x - 2)); OutputType arrType = - extractPrimitiveType(label.substr(x + 2, greaterThan - x - 2)); + extractPrimitiveType(label.substr(lessThan + 1, x - lessThan - 2)); return std::make_pair(arrSize, arrType); } diff --git a/unittests/output_record/RecordParserTester.cpp b/unittests/output_record/RecordParserTester.cpp index 8cebe3ad09..dd35734c6d 100644 --- a/unittests/output_record/RecordParserTester.cpp +++ b/unittests/output_record/RecordParserTester.cpp @@ -51,7 +51,7 @@ CUDAQ_TEST(ParserTester, checkArrayLabeled) { const std::string log = "HEADER\tschema_name\tlabeled\n" "HEADER\tschema_version\t1.0\n" "START\n" - "OUTPUT\tARRAY\t3\tarray<3 x i32>\n" + "OUTPUT\tARRAY\t3\tarray\n" "OUTPUT\tINT\t13\t[0]\n" "OUTPUT\tINT\t42\t[1]\n" "OUTPUT\tINT\t61\t[2]\n" @@ -72,17 +72,17 @@ CUDAQ_TEST(ParserTester, checkArrayLabeled) { CUDAQ_TEST(ParserTester, checkMultipleShots) { const std::string log = "HEADER\tschema_name\tlabeled\n" "START\n" - "OUTPUT\tARRAY\t2\tarray<2 x i1>\n" + "OUTPUT\tARRAY\t2\tarray\n" "OUTPUT\tBOOL\ttrue\t[0]\n" "OUTPUT\tBOOL\ttrue\t[1]\n" "END\t0\n" "START\n" - "OUTPUT\tARRAY\t2\tarray<2 x i1>\n" + "OUTPUT\tARRAY\t2\tarray\n" "OUTPUT\tBOOL\tfalse\t[1]\n" "OUTPUT\tBOOL\ttrue\t[0]\n" "END\t0\n" "START\n" - "OUTPUT\tARRAY\t2\tarray<2 x i1>\n" + "OUTPUT\tARRAY\t2\tarray\n" "OUTPUT\tBOOL\ttrue\t[0]\n" "OUTPUT\tBOOL\tfalse\t[1]\n" "END\t0";