Skip to content

Commit

Permalink
* Skeleton for output record logging and parsing
Browse files Browse the repository at this point in the history
  -- primitive types
TODO: Handle arrays (vector of results)

* Naive way of parsing integer array log

Signed-off-by: Pradnya Khalate <[email protected]>
  • Loading branch information
khalatepradnya committed Jan 31, 2025
1 parent d53ced2 commit 8505897
Show file tree
Hide file tree
Showing 6 changed files with 248 additions and 0 deletions.
69 changes: 69 additions & 0 deletions runtime/common/RecordLogger.h
Original file line number Diff line number Diff line change
@@ -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 <fstream>
#include <sstream>
#include <string>
#include <typeinfo>
#include <vector>

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 <typename T>
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 <typename T>
void log(const std::vector<T> &records) {
emitHeader();
for (auto r : records) {
ss << "START\n";
logSingleRecord<T>(r);
ss << "END\t0\n"; // Assumes success always
}
}

std::string getLog() { return ss.str(); }

void writeToFile(std::ofstream &outFile) { outFile << ss.rdbuf(); }
};

} // namespace cudaq
85 changes: 85 additions & 0 deletions runtime/common/RecordParser.h
Original file line number Diff line number Diff line change
@@ -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 <string>
#include <vector>

namespace cudaq {

struct OutputRecord {
void *buffer;
std::size_t size;
};

struct RecordParser {
private:
bool labelExpected = false;

public:
std::vector<OutputRecord> parse(const std::string &data) {
std::vector<OutputRecord> results;
std::vector<std::string> lines = cudaq::split(data, '\n');
std::size_t arrSize = 0;
int arrIdx = -1;
for (auto line : lines) {
std::vector<std::string> 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<void *>(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<void *>(resArr),
sizeof(int) * arrSize});
}
static_cast<int *>(results.back().buffer)[arrIdx++] =
std::stoi(entries[2]);
if (arrIdx == arrSize) {
arrSize = 0;
arrIdx = -1;
}
} else
results.emplace_back(
OutputRecord{static_cast<void *>(new int(std::stoi(entries[2]))),
sizeof(int)});
} else if ("FLOAT" == entries[1]) {
results.emplace_back(
OutputRecord{static_cast<void *>(new int(std::stof(entries[2]))),
sizeof(float)});
} else if ("DOUBLE" == entries[1]) {
results.emplace_back(
OutputRecord{static_cast<void *>(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
1 change: 1 addition & 0 deletions unittests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,7 @@ endif()

add_subdirectory(backends)
add_subdirectory(Optimizer)
add_subdirectory(output_record)

set(CUDAQ_BRAKET_RUNTIME_TEST_SOURCES
# Integration tests
Expand Down
21 changes: 21 additions & 0 deletions unittests/output_record/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)
24 changes: 24 additions & 0 deletions unittests/output_record/RecordLoggerTester.cpp
Original file line number Diff line number Diff line change
@@ -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.h>

CUDAQ_TEST(LoggerTester, checkBoolean) {
std::vector<bool> 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());
}
48 changes: 48 additions & 0 deletions unittests/output_record/RecordParserTester.cpp
Original file line number Diff line number Diff line change
@@ -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.h>

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<bool *>(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<int *>(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<int *>(results[0].buffer);
std::vector<int> got(count);
for (int i = 0; i < count; ++i)
got[i] = ptr[i];
EXPECT_EQ(1, got[2]);
EXPECT_EQ(0, got[3]);
}

0 comments on commit 8505897

Please sign in to comment.