Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,5 @@ compile_commands.json
cmake_install.cmake
/CMakeCache.txt
/CMakeFiles
.venv/
*.sif
4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -172,10 +172,10 @@ if(QIREE_BUILD_DOCS)
endif()

if(QIREE_BUILD_TESTS AND NOT GTest_FOUND)
find_package(GTest)
find_package(GTest REQUIRED)
if(NOT GTest_FOUND)
message(SEND_ERROR
"Googletest (GTest) is required for testing but was not found"
"Googletest (GTest) is required for testing but was not found. Please install from source. Optional: In the user environment, you can run `cmake -DCMAKE_INSTALL_PREFIX=../install -DBUILD_SHARED_LIBS=ON ..` in the googletest/build folder, which allows you to build without sudo. Then you can set `export CMAKE_PREFIX_PATH=$HOME/googletest/install so that QIR-EE can find the installation."
)
endif()
endif()
Expand Down
246 changes: 246 additions & 0 deletions src/cqiree/QireeManager.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
//----------------------------------*-C++-*----------------------------------//
// Copyright 2025 UT-Battelle, LLC, and other QIR-EE developers.
// See the top-level COPYRIGHT file for details.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//---------------------------------------------------------------------------//
//! \file cqiree/QireeManager.cc
//---------------------------------------------------------------------------//
#include "QireeManager.hh"

#include <iostream>
#include <sstream>
#include <stdexcept>

#include "qiree_config.h"

#include "qiree/Assert.hh"
#include "qiree/Executor.hh"
#include "qiree/Module.hh"
#include "qiree/QuantumInterface.hh"
#include "qiree/ResultDistribution.hh"
#include "qiree/SingleResultRuntime.hh"
#include "qirqsim/QsimQuantum.hh"
#include "qirqsim/QsimRuntime.hh"

#define CQIREE_FAIL(CODE, MESSAGE) \
do \
{ \
std::cerr << "qiree failure: " << MESSAGE << '\n'; \
return ReturnCode::CODE; \
} while (0)

namespace qiree
{
//---------------------------------------------------------------------------//
QireeManager::QireeManager() = default;
QireeManager::~QireeManager() = default;

//---------------------------------------------------------------------------//
QireeManager::ReturnCode
QireeManager::load_module(std::string_view data_contents) throw()
{
try
{
// Load module from memory contents
std::string content{data_contents};
// Convert string_view to string for Module constructor
module_ = Module::from_bytes(content);
}
catch (std::exception const& e)
{
CQIREE_FAIL(fail_load, e.what());
}
return ReturnCode::success;
}

//---------------------------------------------------------------------------//
QireeManager::ReturnCode QireeManager::load_module(std::string filename) throw()
{
try
{
module_ = std::make_unique<Module>(filename);
QIREE_ENSURE(*module_);
}
catch (std::exception const& e)
{
std::cerr << "qiree failure: " << e.what() << '\n';
CQIREE_FAIL(fail_load, e.what());
}

return ReturnCode::success;
}

//---------------------------------------------------------------------------//
QireeManager::ReturnCode QireeManager::num_quantum_reg(int& result) const
throw()
{
if (!module_)
{
CQIREE_FAIL(not_ready, "cannot query module before module load");
}
if (execute_)
{
CQIREE_FAIL(not_ready, "cannot query module after creating executor");
}

auto attrs = module_->load_entry_point_attrs();
result = attrs.required_num_qubits;
return ReturnCode::success;
}

//---------------------------------------------------------------------------//
QireeManager::ReturnCode QireeManager::num_classical_reg(int& result) const
throw()
{
if (!module_)
{
CQIREE_FAIL(not_ready, "cannot query module before module load");
}
if (execute_)
{
CQIREE_FAIL(not_ready, "cannot query module after creating executor");
}

auto attrs = module_->load_entry_point_attrs();
result = attrs.required_num_results;
return ReturnCode::success;
}

//---------------------------------------------------------------------------//
QireeManager::ReturnCode
QireeManager::setup_executor(std::string_view backend,
std::string_view config_json) throw()
{
if (!module_)
{
CQIREE_FAIL(not_ready, "cannot create executor before module load");
}
if (execute_)
{
CQIREE_FAIL(not_ready, "cannot create executor again");
}

try
{
if (!config_json.empty())
{
QIREE_NOT_IMPLEMENTED("CQiree JSON configuration input");
}

if (backend == "qsim")
{
#if QIREE_USE_QSIM
// Create runtime interface: give runtime a pointer to quantum
// (lifetime of the reference is guaranteed by our shared pointer
// copy)
constexpr unsigned long int seed = 0;
auto quantum = std::make_shared<QsimQuantum>(std::cout, seed);
runtime_ = std::make_shared<QsimRuntime>(std::cout, *quantum);
quantum_ = std::move(quantum);
#else
QIREE_NOT_CONFIGURED("QSim");
#endif
}
else if (backend == "xacc")
{
#if QIREE_USE_XACC
QIREE_NOT_IMPLEMENTED("XACC backend for CQiree");
#else
QIREE_NOT_CONFIGURED("XACC");
#endif
}
else
{
QIREE_VALIDATE(false,
<< "unknown backend name '" << backend << "'");
}
}
catch (std::exception const& e)
{
CQIREE_FAIL(fail_load,
"error while creating quantum runtimes: " << e.what());
}

try
{
// Create executor with the module, quantum and runtime interfaces
QIREE_ASSERT(module_ && *module_);
execute_ = std::make_unique<Executor>(std::move(*module_));
}
catch (std::exception const& e)
{
CQIREE_FAIL(fail_load, "error while creating executor: " << e.what());
}

return ReturnCode::success;
}

//---------------------------------------------------------------------------//
QireeManager::ReturnCode QireeManager::execute(int num_shots) throw()
{
if (execute_)
{
CQIREE_FAIL(not_ready, "cannot create executor again");
}

if (num_shots <= 0)
{
CQIREE_FAIL(invalid_input, "num_shots was nonpositive");
}

try
{
QIREE_ASSERT(runtime_ && quantum_);
result_ = std::make_unique<ResultDistribution>();

for (auto i = 0; i < num_shots; ++i)
{
(*execute_)(*quantum_, *runtime_);
result_->accumulate(runtime_->result());
}
}
catch (std::exception const& e)
{
CQIREE_FAIL(fail_execute, e.what());
}
return ReturnCode::success;
}

//---------------------------------------------------------------------------//
QireeManager::ReturnCode QireeManager::num_results(int& count) const throw()
{
if (!result_)
{
CQIREE_FAIL(not_ready, "execute has not been called");
}

count = static_cast<int>(result_->size());

return ReturnCode::success;
}

//---------------------------------------------------------------------------//
QireeManager::ReturnCode
QireeManager::get_result(int index, std::string_view key, int* count) const
throw()
{
if (!result_)
{
CQIREE_FAIL(not_ready, "execute has not been called");
}

try
{
(void)sizeof(key);
(void)sizeof(count);
QIREE_NOT_IMPLEMENTED("getting results");
}
catch (std::exception const& e)
{
CQIREE_FAIL(fail_execute,
"could not retrieve index " << index << ": " << e.what());
}
return ReturnCode::success;
}
//---------------------------------------------------------------------------//
} // namespace qiree
26 changes: 26 additions & 0 deletions src/qiree/Module.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@

#include <sstream>
#include <string_view>
#include <memory>
#include <llvm/IR/Attributes.h>
#include <llvm/IR/Constants.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/Module.h>
#include <llvm/IRReader/IRReader.h>
#include <llvm/Support/SourceMgr.h>
#include <llvm/Support/MemoryBuffer.h>

#include "Assert.hh"

Expand Down Expand Up @@ -155,11 +157,35 @@ Module::Module(std::string const& filename, std::string const& entrypoint)
}

//---------------------------------------------------------------------------//
/*!
* Reading a module by parsing an in-memory LLVM IR string.
*/

Module::Module() = default;
Module::~Module() = default;
Module::Module(Module&&) = default;
Module& Module::operator=(Module&&) = default;

std::unique_ptr<Module> Module::from_bytes(std::string const & content) {
llvm::SMDiagnostic err;

// Create memory buffer from the in-memory IR content
std::unique_ptr<llvm::MemoryBuffer> buffer = llvm::MemoryBuffer::getMemBuffer(content, "<in-memory>", false);

// Parse the IR using LLVM context
auto llvm_module = llvm::parseIR(buffer->getMemBufferRef(), err, context());

if (!llvm_module)
{
err.print("qiree", llvm::errs());
QIREE_VALIDATE(llvm_module,
<< "Failed to parse QIR from in-memory content '" << content << "'");
}

// Construct and return Module from parsed llvm::Module
return std::make_unique<Module>(std::move(llvm_module));
}

//---------------------------------------------------------------------------//
/*!
* Process entry point attributes.
Expand Down
4 changes: 4 additions & 0 deletions src/qiree/Module.hh
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#pragma once

#include <memory>
#include <string_view>

#include "Types.hh"

Expand Down Expand Up @@ -42,6 +43,9 @@ class Module
Module(Module const&) = delete;
Module& operator=(Module const&) = delete;

// Reading a module by parsing an in-memory LLVM IR string.
static std::unique_ptr<Module> from_bytes(std::string const & content);

// Construct from an externally created LLVM module
explicit Module(UPModule&& module);

Expand Down
5 changes: 5 additions & 0 deletions test/Test.hh
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@
#include <string>
#include <gtest/gtest.h>

#include <fstream>
#include <sstream>
#include <string_view>
#include <stdexcept>

namespace qiree
{
namespace test
Expand Down
21 changes: 21 additions & 0 deletions test/qiree/Module.test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,27 @@ TEST_F(ModuleTest, several_gates)
EXPECT_FALSE(flags.dynamic_result_management);
}

//---------------------------------------------------------------------------//
TEST_F(ModuleTest, parse_ir_from_file) {

// Helper function to read a file and return its contents as a string to be fed into from_bytes
auto read_ll_file = [](const std::string& path) -> std::string {
std::ifstream file(path);
if (!file) throw std::runtime_error("Cannot open file: " + path);
std::ostringstream buf;
buf << file.rdbuf();
return buf.str();
};

// Read the LLVM IR from a file and parse it
std::string ir = read_ll_file(this->test_data_path("bell.ll"));

// Expect no exceptions during parsing
EXPECT_NO_THROW({
std::unique_ptr<Module> m = Module::from_bytes(ir);
});
}

//---------------------------------------------------------------------------//
} // namespace test
} // namespace qiree