Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OQC client upgrade #2352

Merged
merged 15 commits into from
Nov 15, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
12 changes: 7 additions & 5 deletions python/tests/backends/test_OQC.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,9 @@ def assert_close(got) -> bool:
@pytest.fixture(scope="session", autouse=True)
def startUpMockServer():

os.environ["OQC_PASSWORD"] = "password"
# Set the targeted QPU
cudaq.set_target('oqc',
url=f'http://localhost:{port}',
email="[email protected]")
os.environ["OQC_AUTH_TOKEN"] = "fake_auth_token"
os.environ["OQC_DEVICE"] = "qpu:uk:-1:1234567890"
os.environ["OQC_URL"] = f"http://localhost:{port}"

# Launch the Mock Server
p = Process(target=startServer, args=(port,))
Expand All @@ -48,6 +46,10 @@ def startUpMockServer():
pytest.exit("Mock server did not start in time, skipping tests.",
returncode=1)

# Set the targeted QPU
cudaq.set_target('oqc',
url=f'http://localhost:{port}',
auth_token="fake_auth_token")
yield "Running the tests."

# Kill the server, remove the file
Expand Down
118 changes: 80 additions & 38 deletions runtime/cudaq/platform/default/rest/helpers/oqc/OQCServerHelp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ class OQCServerHelper : public ServerHelper {
/// @brief Create n requested tasks placeholders returning uuids for each
std::vector<std::string> createNTasks(int n);

/// @brief Gets endpoint of a device
std::tuple<std::string, std::string>
get_qpu_endpoint(std::string, std::string, std::string);

/// @brief make a compiler config json string parameterising with number of
/// shots
std::string makeConfig(int shots);
Expand Down Expand Up @@ -113,6 +117,18 @@ std::string get_from_config(BackendConfig config, const std::string &key,
return item;
}

std::string get_from_config_no_low(BackendConfig config, const std::string &key,
const auto &missing_functor) {
/// This function does the same with get_from_config, but without lowering
/// Auth tokens are case sensitive and shouldn't be lowered

const auto iter = config.find(key);
auto item = iter != config.end() ? iter->second : missing_functor();
std::transform(item.begin(), item.end(), item.begin(),
[](auto c) { return c; });
return item;
}

void check_machine_allowed(const std::string &machine) {
if (Machines.find(machine) == Machines.end()) {
std::string allowed;
Expand Down Expand Up @@ -144,20 +160,26 @@ void OQCServerHelper::initialize(BackendConfig config) {
backendConfig = std::move(config);
return;
}

config["entry_url"] =
get_from_config(config, "entry_url", make_env_functor("OQC_URL"));
config["target"] =
get_from_config(config, "device", make_env_functor("OQC_DEVICE"));
config["auth_token"] = get_from_config_no_low(
config, "auth_token", make_env_functor("OQC_AUTH_TOKEN"));
auto [device_url, dev_id] = get_qpu_endpoint(
config["entry_url"], config["target"], config["auth_token"]);
// Set the necessary configuration variables for the OQC API
config["url"] = get_from_config(
config, "url",
make_env_functor("OQC_URL", "https://sandbox.qcaas.oqc.app"));

config["url"] = device_url;

config["version"] = "v0.3";
config["user_agent"] = "cudaq/0.3.0";
config["target"] = "Lucy";
config["oqc_user_agent"] = "QCaaS Client 3.9.1";
config["qubits"] = Machines.at(machine);
config["email"] =
get_from_config(config, "email", make_env_functor("OQC_EMAIL"));
config["password"] = make_env_functor("OQC_PASSWORD")();
// Construct the API job path
config["job_path"] = "/tasks"; // config["url"] + "/tasks";

// Construct the API job path
config["job_path"] = std::string("/") + dev_id + "/tasks";
parseConfigForCommonParams(config);

// Move the passed config into the member variable backendConfig
Expand All @@ -170,32 +192,63 @@ bool OQCServerHelper::keyExists(const std::string &key) const {
}

std::vector<std::string> OQCServerHelper::createNTasks(int n) {
/// This function returns the task ids as a vector of string.
/// The same as "create_task_ids" in QCaaS client, client.py

RestHeaders headers = OQCServerHelper::getHeaders();
nlohmann::json j;
nlohmann::json job;
int nTask = n;
job["task_count"] = nTask;
job["qpu_id"] = backendConfig.at("target"); // qpu:uk:2:d865b5a184
job["tag"] = "";
std::vector<std::string> output;
for (int i = 0; i < n; ++i) {
auto response = client.post(backendConfig.at("url"),
backendConfig.at("job_path"), j, headers);
output.push_back(response[0]);

auto response = client.post(backendConfig.at("url"),
backendConfig.at("job_path"), job, headers);
return response;
}

std::tuple<std::string, std::string>
OQCServerHelper::get_qpu_endpoint(std::string server_url, std::string qpu_id,
std::string auth_token) {
RestHeaders headers;

headers["Authentication-Token"] = auth_token;
auto response = client.get(server_url, "/admin/qpu", headers, true);

for (auto item : response["items"]) {
if (item["id"] == qpu_id) {
std::string device_url = item["url"];
size_t pos = device_url.find('/', 8); // Start fter "https://"
std::string qpu_server_url = device_url.substr(0, pos);
std::string qpu_device_id = device_url.substr(pos + 1);
return std::make_tuple(qpu_server_url, qpu_device_id);
}
}
return output;

std::stringstream stream;
stream << "No device:" + qpu_id + " is on " + server_url + "." << std::endl;
throw std::runtime_error(stream.str());
}

std::string OQCServerHelper::makeConfig(int shots) {
return "{\"$type\": \"<class 'scc.compiler.config.CompilerConfig'>\", "
return "{\"$type\": \"<class 'qat.purr.compiler.config.CompilerConfig'>\", "
"\"$data\": {\"repeats\": " +
std::to_string(shots) +
", \"repetition_period\": null, \"results_format\": {\"$type\": "
"\"<class 'scc.compiler.config.QuantumResultsFormat'>\", \"$data\": "
"\"<class 'qat.purr.compiler.config.QuantumResultsFormat'>\", "
"\"$data\": "
"{\"format\": {\"$type\": \"<enum "
"'scc.compiler.config.InlineResultsProcessing'>\", \"$value\": 1}, "
"'qat.purr.compiler.config.InlineResultsProcessing'>\", \"$value\": "
"1}, "
"\"transforms\": {\"$type\": \"<enum "
"'scc.compiler.config.ResultsFormatting'>\", \"$value\": 3}}}, "
"'qat.purr.compiler.config.ResultsFormatting'>\", \"$value\": 3}}}, "
"\"metrics\": {\"$type\": \"<enum "
"'scc.compiler.config.MetricsType'>\", \"$value\": 6}, "
"'qat.purr.compiler.config.MetricsType'>\", \"$value\": 6}, "
"\"active_calibrations\": [], \"optimizations\": {\"$type\": \"<class "
"'scc.compiler.config.Tket'>\", \"$data\": {\"tket_optimizations\": "
"{\"$type\": \"<enum 'scc.compiler.config.TketOptimizations'>\", "
"'qat.purr.compiler.config.Tket'>\", \"$data\": "
"{\"tket_optimizations\": "
"{\"$type\": \"<enum 'qat.purr.compiler.config.TketOptimizations'>\", "
"\"$value\": 30}}}}}";
}

Expand All @@ -217,6 +270,8 @@ OQCServerHelper::createJob(std::vector<KernelExecution> &circuitCodes) {
job["task_id"] = task_ids[i];
job["config"] = makeConfig(static_cast<int>(shots));
job["program"] = circuitCodes[i].code;
job["qpu_id"] = backendConfig.at("target");
job["tag"] = "";
j["tasks"].push_back(job);
jobs[i] = j;
}
Expand Down Expand Up @@ -391,25 +446,12 @@ OQCServerHelper::processResults(ServerMessage &postJobResponse,

// Get the headers for the API requests
RestHeaders OQCServerHelper::getHeaders() {
// Check if the necessary keys exist in the configuration
if (!keyExists("email") || !keyExists("password"))
throw std::runtime_error("Key doesn't exist in backendConfig.");

// Construct the headers
RestHeaders headers;
headers["Content-Type"] = "application/json";

nlohmann::json j;
j["email"] = backendConfig.at("email");
j["password"] = backendConfig.at("password");
nlohmann::json response =
client.post(backendConfig.at("url") + "/auth", "", j, headers,
/*enableLossgging=*/false);
std::string key = response.at("access_token");
backendConfig["access_token"] = key;

headers["Authorization"] = "Bearer " + backendConfig["access_token"];

headers["Authentication-Token"] = backendConfig["auth_token"];
headers["User-agent"] = backendConfig["oqc_user_agent"];
headers["Content-type"] = "application/json";
// Return the headers
return headers;
}
Expand Down
23 changes: 13 additions & 10 deletions unittests/backends/oqc/OQCTester.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@
#include <gtest/gtest.h>

std::string mockPort = "62442";
std::string email = "[email protected]";
std::string password = "password";
std::string auth_token = "fake_auth_token";
std::string device_id = "qpu:uk:-1:1234567890";
std::string entry_url = "http://localhost:" + mockPort;
std::string backendStringTemplate =
"oqc;emulate;false;url;http://localhost:{};email;{};password;{}";
"oqc;emulate;false;url;http://localhost:{};auth_token;{};device;{};";

bool isValidExpVal(double value) {
// give us some wiggle room while keep the tests fast
Expand All @@ -25,7 +26,7 @@ bool isValidExpVal(double value) {

CUDAQ_TEST(OQCTester, checkSampleSync) {
auto backendString = fmt::format(fmt::runtime(backendStringTemplate),
mockPort, email, password);
mockPort, auth_token, device_id);

auto &platform = cudaq::get_platform();
platform.setTargetBackend(backendString);
Expand All @@ -42,7 +43,7 @@ CUDAQ_TEST(OQCTester, checkSampleSync) {

CUDAQ_TEST(OQCTester, checkSampleAsync) {
auto backendString = fmt::format(fmt::runtime(backendStringTemplate),
mockPort, email, password);
mockPort, auth_token, device_id);

auto &platform = cudaq::get_platform();
platform.setTargetBackend(backendString);
Expand All @@ -59,7 +60,7 @@ CUDAQ_TEST(OQCTester, checkSampleAsync) {

CUDAQ_TEST(OQCTester, checkSampleAsyncLoadFromFile) {
auto backendString = fmt::format(fmt::runtime(backendStringTemplate),
mockPort, email, password);
mockPort, auth_token, device_id);

auto &platform = cudaq::get_platform();
platform.setTargetBackend(backendString);
Expand Down Expand Up @@ -92,7 +93,7 @@ CUDAQ_TEST(OQCTester, checkSampleAsyncLoadFromFile) {

CUDAQ_TEST(OQCTester, checkObserveSync) {
auto backendString = fmt::format(fmt::runtime(backendStringTemplate),
mockPort, email, password);
mockPort, auth_token, device_id);

auto &platform = cudaq::get_platform();
platform.setTargetBackend(backendString);
Expand All @@ -115,7 +116,7 @@ CUDAQ_TEST(OQCTester, checkObserveSync) {

CUDAQ_TEST(OQCTester, checkObserveAsync) {
auto backendString = fmt::format(fmt::runtime(backendStringTemplate),
mockPort, email, password);
mockPort, auth_token, device_id);

auto &platform = cudaq::get_platform();
platform.setTargetBackend(backendString);
Expand All @@ -140,7 +141,7 @@ CUDAQ_TEST(OQCTester, checkObserveAsync) {

CUDAQ_TEST(OQCTester, checkObserveAsyncLoadFromFile) {
auto backendString = fmt::format(fmt::runtime(backendStringTemplate),
mockPort, email, password);
mockPort, auth_token, device_id);

auto &platform = cudaq::get_platform();
platform.setTargetBackend(backendString);
Expand Down Expand Up @@ -177,7 +178,9 @@ CUDAQ_TEST(OQCTester, checkObserveAsyncLoadFromFile) {
}

int main(int argc, char **argv) {
setenv("OQC_PASSWORD", password.c_str(), 0);
setenv("OQC_URL", entry_url.c_str(), 0);
setenv("OQC_AUTH_TOKEN", auth_token.c_str(), 0);
setenv("OQC_DEVICE", device_id.c_str(), 0);
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
Loading
Loading