Skip to content
Draft
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
1 change: 1 addition & 0 deletions .github/workflows/config/spelling_allowlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ QPUs
QPU’s
QTX
QX
Qilimanjaro
Qiskit
QuEra
QuTiP
Expand Down
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,11 @@ if (NOT DEFINED CUDAQ_ENABLE_PASQAL_BACKEND)
set(CUDAQ_ENABLE_PASQAL_BACKEND ON CACHE BOOL "Enable building the Pasqal target.")
endif()

# Enable Qilimanjaro target by default.
if (NOT DEFINED CUDAQ_ENABLE_QILIMANJARO_BACKEND)
set(CUDAQ_ENABLE_QILIMANJARO_BACKEND ON CACHE BOOL "Enable building the Qilimanjaro target.")
endif()

# Enable Quantum Circuits, Inc. (QCI) target by default.
if (NOT DEFINED CUDAQ_ENABLE_QCI_BACKEND)
set(CUDAQ_ENABLE_QCI_BACKEND ON CACHE BOOL "Enable building the Quantum Circuits, Inc. target.")
Expand Down
2 changes: 1 addition & 1 deletion python/cudaq/dynamics/evolution.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from .integrator import BaseIntegrator
from .schedule import Schedule

analog_targets = ["pasqal", "quera"]
analog_targets = ["pasqal", "qilimanjaro", "quera"]


def _taylor_series_expm(op_matrix: NDArray[numpy.complexfloating],
Expand Down
2 changes: 2 additions & 0 deletions python/extension/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ declare_mlir_python_extension(CUDAQuantumPythonSources.Extension
../../runtime/cudaq/platform/orca/OrcaServerHelper.cpp
../../runtime/cudaq/platform/pasqal/PasqalRemoteRESTQPU.cpp
../../runtime/cudaq/platform/pasqal/PasqalServerHelper.cpp
../../runtime/cudaq/platform/qilimanjaro/QilimanjaroRemoteRESTQPU.cpp
../../runtime/cudaq/platform/qilimanjaro/QilimanjaroServerHelper.cpp
../../runtime/cudaq/platform/quera/QuEraRemoteRESTQPU.cpp

EMBED_CAPI_LINK_LIBS
Expand Down
30 changes: 30 additions & 0 deletions python/tests/backends/test_Qilimanjaro.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# ============================================================================ #
# Copyright (c) 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. #
# ============================================================================ #

import cudaq
import json
import os
import pytest

skipIfQilimanjaroNotInstalled = pytest.mark.skipif(
not cudaq.has_target("qilimanjaro"),
reason='Could not find `qilimanjaro` in installation'
)


@pytest.fixture(scope="session", autouse=True)
def do_something():
# NOTE: Credentials can be set with environment variables
cudaq.set_target("qilimanjaro")
yield "Running the tests."
cudaq.reset_target()

# leave for gdb debugging
if __name__ == "__main__":
loc = os.path.abspath(__file__)
pytest.main([loc, "-rP"])
4 changes: 4 additions & 0 deletions runtime/cudaq/platform/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,7 @@ endif()
if (CUDAQ_ENABLE_PASQAL_BACKEND)
add_subdirectory(pasqal)
endif()

if (CUDAQ_ENABLE_QILIMANJARO_BACKEND)
add_subdirectory(qilimanjaro)
endif()
38 changes: 38 additions & 0 deletions runtime/cudaq/platform/qilimanjaro/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# ============================================================================ #
# 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. #
# ============================================================================ #

set(LIBRARY_NAME cudaq-qilimanjaro-qpu)
message(STATUS "Building Qilimanjaro REST QPU.")

add_library(${LIBRARY_NAME}
SHARED
QilimanjaroRemoteRESTQPU.cpp
QilimanjaroServerHelper.cpp
)

target_include_directories(${LIBRARY_NAME} PRIVATE .
PUBLIC
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/runtime>
$<INSTALL_INTERFACE:include>
)

target_link_libraries(${LIBRARY_NAME}
PUBLIC
cudaq-operator
cudaq-common
PRIVATE
pthread
cudaq-mlir-runtime
fmt::fmt-header-only
cudaq
cudaq-platform-default
)

install(TARGETS ${LIBRARY_NAME} DESTINATION lib)

add_target_config(qilimanjaro)
23 changes: 23 additions & 0 deletions runtime/cudaq/platform/qilimanjaro/QilimanjaroRemoteRESTQPU.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*******************************************************************************
* Copyright (c) 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 "common/AnalogRemoteRESTQPU.h"

namespace {

/// @brief The `QilimanjaroRemoteRESTQPU` is a subtype of QPU that enables the
/// execution of Analog Hamiltonian Program via a REST Client.
class QilimanjaroRemoteRESTQPU : public cudaq::AnalogRemoteRESTQPU {
public:
QilimanjaroRemoteRESTQPU() : AnalogRemoteRESTQPU() {}
QilimanjaroRemoteRESTQPU(QilimanjaroRemoteRESTQPU &&) = delete;
virtual ~QilimanjaroRemoteRESTQPU() = default;
};
} // namespace

CUDAQ_REGISTER_TYPE(cudaq::QPU, QilimanjaroRemoteRESTQPU, qilimanjaro)
116 changes: 116 additions & 0 deletions runtime/cudaq/platform/qilimanjaro/QilimanjaroServerHelper.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/*******************************************************************************
* Copyright (c) 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 "QilimanjaroServerHelper.h"
#include "common/AnalogHamiltonian.h"
#include "common/Logger.h"

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

nit

#include <unordered_map>
#include <unordered_set>

namespace cudaq {

void QilimanjaroServerHelper::initialize(BackendConfig config) {
cudaq::info("Initialize Qilimanjaro's SpeQtrum.");

// Hard-coded for now.
const std::string MACHINE = "CUDA_DYNAMICS";

cudaq::info("Running on device {}", MACHINE);

if (!config.contains("machine"))
config["machine"] = MACHINE;

if (!config["nshots"].empty())
setShots(std::stoul(config["nshots"]));

parseConfigForCommonParams(config);

backendConfig = std::move(config);
}

RestHeaders QilimanjaroServerHelper::getHeaders() {
std::string token;

if (auto auth_token = std::getenv("QILIMANJARO_AUTH_TOKEN"))
token = "Bearer " + std::string(auth_token);
else
token = "Bearer ";

std::map<std::string, std::string> headers{
{"Authorization", token},
{"Content-Type", "application/json"},
{"User-Agent", "cudaq/0.12.0"},
{"Connection", "keep-alive"},
{"Accept", "*/*"}};

return headers;
}

ServerJobPayload
QilimanjaroServerHelper::createJob(std::vector<KernelExecution> &circuitCodes) {
std::vector<ServerMessage> tasks;

for (auto &circuitCode : circuitCodes) {
ServerMessage message;
message["machine"] = backendConfig.at("machine");
message["shots"] = shots;
message["sequence"] = nlohmann::json::parse(circuitCode.code);
tasks.push_back(message);
}

cudaq::info("Created job payload for Qilimanjaro, targeting device {}",
backendConfig.at("machine"));

// Return a tuple containing the job path, headers, and the job message
return std::make_tuple(speqtrumApiUrl + "/execute", getHeaders(), tasks);
}

std::string QilimanjaroServerHelper::extractJobId(ServerMessage &postResponse) {
return postResponse["data"]["id"].get<std::string>();
}

std::string QilimanjaroServerHelper::constructGetJobPath(std::string &jobId) {
return speqtrumApiUrl + "/jobs/" + jobId;
}

std::string
QilimanjaroServerHelper::constructGetJobPath(ServerMessage &postResponse) {
return speqtrumApiUrl + "/jobs/" + postResponse["data"]["id"].get<std::string>();
}

bool QilimanjaroServerHelper::jobIsDone(ServerMessage &getJobResponse) {
std::unordered_set<std::string> terminal_states = {"COMPLETED", "ERROR", "CANCELED"};
auto jobStatus = getJobResponse["data"]["status"].get<std::string>();
return terminal_states.find(jobStatus) != terminal_states.end();
}

sample_result QilimanjaroServerHelper::processResults(ServerMessage &postJobResponse,
std::string &jobId) {
auto status = postJobResponse["data"]["status"].get<std::string>();
if (status != "COMPLETED")
throw std::runtime_error("Job status: " + status);

std::vector<ExecutionResult> results;
auto jobs = postJobResponse["data"]["result"];
for (auto &job : jobs) {
std::unordered_map<std::string, std::size_t> result;
for (auto &[bitstring, count] : job.items()) {
auto r_bitstring = bitstring;
result[r_bitstring] = count;
}
results.push_back(ExecutionResult(result));
}

return sample_result(results);
}

} // namespace cudaq

// Register the Qilimanjaro server helper in the CUDA-Q server helper factory
CUDAQ_REGISTER_TYPE(cudaq::ServerHelper, cudaq::QilimanjaroServerHelper, qilimanjaro)
67 changes: 67 additions & 0 deletions runtime/cudaq/platform/qilimanjaro/QilimanjaroServerHelper.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/****************************************************************-*- C++ -*-****
* Copyright (c) 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 "common/ServerHelper.h"
#include "nlohmann/json.hpp"

namespace cudaq {

class QilimanjaroServerHelper : public ServerHelper {
protected:
/// @brief Server helper implementation for communicating with the REST API of
/// Qilimanjaro's SpeQtrum platform.
const std::string speqtrumApiUrl = "https://qilimanjaro.ddns.net/public-api/api/v1";

public:
/// @brief Returns the name of the server helper.
const std::string name() const override { return "qilimanjaro"; }

/// @brief Initializes the server helper with the provided backend
/// configuration.
void initialize(BackendConfig config) override;

/// @brief Return the POST/GET required headers.
/// @return
RestHeaders getHeaders() override;

/// @brief Creates a quantum computation job using the provided kernel
/// executions and returns the corresponding payload.
ServerJobPayload
createJob(std::vector<KernelExecution> &circuitCodes) override;

/// @brief Extract the job id from the server response from posting the job.
std::string extractJobId(ServerMessage &postResponse) override;

/// @brief Get the specific path required to retrieve job results. Construct
/// specifically from the job id.
std::string constructGetJobPath(std::string &jobId) override;

/// @brief Get the specific path required to retrieve job results. Construct
/// from the full server response message.
std::string constructGetJobPath(ServerMessage &postResponse) override;

/// @brief Get the jobs results polling interval.
/// @return
std::chrono::microseconds
nextResultPollingInterval(ServerMessage &postResponse) override {
return std::chrono::seconds(1);
}

/// @brief Return true if the job is done.
bool jobIsDone(ServerMessage &getJobResponse) override;

/// @brief Given a successful job and the success response,
/// retrieve the results and map them to a sample_result.
/// @param postJobResponse
/// @param jobId
/// @return
sample_result processResults(ServerMessage &postJobResponse,
std::string &jobId) override;
};

} // namespace cudaq
28 changes: 28 additions & 0 deletions runtime/cudaq/platform/qilimanjaro/qilimanjaro.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# ============================================================================ #
# 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. #
# ============================================================================ #

name: qilimanjaro
description: "CUDA-Q target for qilimanjaro."
config:
# Tell DefaultQuantumPlatform what QPU subtype to use
platform-qpu: qilimanjaro
# Add the qilimanjaro-qpu library to the link list
link-libs: ["-lcudaq-qilimanjaro-qpu"]
# Allow evolve API in C++
preprocessor-defines: ["-D CUDAQ_ANALOG_TARGET"]
# Library mode is only for simulators, physical backends must turn this off
library-mode: false
# Tell NVQ++ to generate glue code to set the target backend name
gen-target-backend: true

target-arguments:
- key: machine
required: false
type: string
platform-arg: machine
help-string: "Specify the Qilimanjaro machine."
1 change: 1 addition & 0 deletions scripts/validate_container.sh
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ available_backends=`\
if [ "${qpu}" != "remote_rest" ] && [ "${qpu}" != "NvcfSimulatorQPU" ] \
&& [ "${qpu}" != "fermioniq" ] && [ "${qpu}" != "orca" ] \
&& [ "${qpu}" != "pasqal" ] && [ "${qpu}" != "quera" ] \
&& [ "${qpu}" != "qilimanjaro" ] \
&& ($gpu_available || [ -z "$gpus" ] || [ "${gpus,,}" == "false" ]); then \
basename $file | cut -d "." -f 1; \
fi; \
Expand Down
7 changes: 7 additions & 0 deletions targettests/lit.site.cfg.py.in
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@ if cmake_boolvar_to_bool(config.cudaq_backends_pasqal):
else:
config.substitutions.append(('%pasqal_avail', 'false'))

config.cudaq_backends_qilimanjaro = "@CUDAQ_ENABLE_QILIMANJARO_BACKEND@"
if cmake_boolvar_to_bool(config.cudaq_backends_qilimanjaro):
config.available_features.add('qilimanjaro')
config.substitutions.append(('%qilimanjaro_avail', 'true'))
else:
config.substitutions.append(('%qilimanjaro_avail', 'false'))

config.cudaq_backends_qci = "@CUDAQ_ENABLE_QCI_BACKEND@"
if cmake_boolvar_to_bool(config.cudaq_backends_qci):
config.available_features.add('qci')
Expand Down
1 change: 1 addition & 0 deletions unittests/backends/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,4 @@ endif()
add_subdirectory(pasqal)
add_subdirectory(qpp_observe)
add_subdirectory(quera)
add_subdirectory(qilimanjaro)
Loading