Skip to content

Commit

Permalink
OQC client upgrade (#2352)
Browse files Browse the repository at this point in the history
* Updating OQC client to new cloud workflow

* updated OQC Client to catch up with the latest QCaaS

* clang-format applied

* mock server is formatted with yapf --style google -i

* pyspelling does not allow qpu and removed the comment line

* DCO Remediation Commit for jamie <[email protected]>

I, jamie <[email protected]>, hereby add my Signed-off-by to this commit: 7d7c716

Signed-off-by: jamie <[email protected]>

* iostream should not be needed

Signed-off-by: Noriyuki Kushida <[email protected]>

DCO Remediation Commit for Noriyuki Kushida <[email protected]>

I, Noriyuki Kushida <[email protected]>, hereby add my Signed-off-by to this commit: d019f7e
I, Noriyuki Kushida <[email protected]>, hereby add my Signed-off-by to this commit: f26f908
I, Noriyuki Kushida <[email protected]>, hereby add my Signed-off-by to this commit: 88734a5

Signed-off-by: Noriyuki Kushida <[email protected]>

DCO Remediation Commit for root <[email protected]>

I, root <[email protected]>, hereby add my Signed-off-by to this commit: b453c84

Signed-off-by: root <[email protected]>

* DCO Remediation Commit for root <[email protected]>

I, root <[email protected]>, hereby add my Signed-off-by to this commit: b453c84

Signed-off-by: root <[email protected]>

Signed-off-by: root <[email protected]>

* OQC_URL is given as an environment variable

Signed-off-by: Noriyuki Kushida <[email protected]>

* format fixed

Signed-off-by: Noriyuki Kushida <[email protected]>

* device ID should be qpu:uk:-1:1234567890 instead of Toshiko1

Signed-off-by: root <[email protected]>

* cudaq.set_target should be just above yield line

Signed-off-by: Noriyuki Kushida <[email protected]>

---------

Signed-off-by: jamie <[email protected]>
Signed-off-by: root <[email protected]>
Signed-off-by: Noriyuki Kushida <[email protected]>
Co-authored-by: Noriyuki Kushida <[email protected]>
  • Loading branch information
jfriel-oqc and nkushida-oqc authored Nov 15, 2024
1 parent d003d13 commit 3300b12
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 72 deletions.
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

0 comments on commit 3300b12

Please sign in to comment.