diff --git a/python/tests/backends/test_QuEra.py b/python/tests/backends/test_QuEra.py index db99ce90cf6..98e5b0fbee6 100644 --- a/python/tests/backends/test_QuEra.py +++ b/python/tests/backends/test_QuEra.py @@ -10,37 +10,17 @@ from cudaq.operator import * import json import numpy as np -import os import pytest -from multiprocessing import Process -from network_utils import check_server_connection - -try: - from utils.mock_qpu.quera import startServer -except: - print("Mock qpu not available, skipping QuEra tests.") - pytest.skip("Mock qpu not available.", allow_module_level=True) - -# Define the port for the mock server -port = 62444 @pytest.fixture(scope="session", autouse=True) -def startUpMockServer(): - # NOTE: Credentials can be set with AWS CLI - cudaq.set_target('quera') - # Launch the Mock Server - p = Process(target=startServer, args=(port,)) - p.start() - if not check_server_connection(port): - p.terminate() - pytest.exit("Mock server did not start in time, skipping tests.", - returncode=1) +def do_something(): + cudaq.set_target("quera") yield "Running the tests." - p.terminate() + cudaq.reset_target() -@pytest.mark.skip(reason="Amazon Braket credentials required") +@pytest.mark.skip(reason="Amazon Braket must be installed") def test_JSON_payload(): ''' Test based on https://docs.aws.amazon.com/braket/latest/developerguide/braket-quera-submitting-analog-program-aquila.html @@ -88,7 +68,7 @@ def test_JSON_payload(): json.dumps(input)) -@pytest.mark.skip(reason="Braket credentials required") +@pytest.mark.skip(reason="Amazon Braket credentials required") def test_ahs_hello(): ''' Test based on diff --git a/runtime/cudaq/platform/quera/CMakeLists.txt b/runtime/cudaq/platform/quera/CMakeLists.txt index 58679e687fa..e57dd3f1ae2 100644 --- a/runtime/cudaq/platform/quera/CMakeLists.txt +++ b/runtime/cudaq/platform/quera/CMakeLists.txt @@ -31,3 +31,12 @@ target_link_libraries(${LIBRARY_NAME} install(TARGETS ${LIBRARY_NAME} DESTINATION lib) add_target_config(quera) + +add_library(cudaq-serverhelper-quera SHARED QuEraExecutor.cpp QuEraServerHelper.cpp) +target_link_libraries(cudaq-serverhelper-quera + PUBLIC + cudaq-serverhelper-braket + cudaq-common + fmt::fmt-header-only +) +install(TARGETS cudaq-serverhelper-quera DESTINATION lib) diff --git a/utils/mock_qpu/__init__.py b/utils/mock_qpu/__init__.py index 5fcb11a2899..1605e27712c 100644 --- a/utils/mock_qpu/__init__.py +++ b/utils/mock_qpu/__init__.py @@ -12,4 +12,3 @@ from .ionq import * from .iqm import * from .quantinuum import * -from .quera import * diff --git a/utils/mock_qpu/quera/__init__.py b/utils/mock_qpu/quera/__init__.py deleted file mode 100644 index 5de6bc3dc43..00000000000 --- a/utils/mock_qpu/quera/__init__.py +++ /dev/null @@ -1,149 +0,0 @@ -# ============================================================================ # -# 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. # -# ============================================================================ # - -import cudaq -from fastapi import FastAPI, HTTPException, Header -from typing import Union -import uvicorn, uuid, base64, ctypes -from pydantic import BaseModel -from llvmlite import binding as llvm - -# Define the REST Server App -app = FastAPI() - - -# Jobs look like the following type -class Job(BaseModel): - name: str - program: str - count: int - - -# Keep track of Job Ids to their Names -createdJobs = {} - -# Could how many times the client has requested the Job -countJobGetRequests = 0 - -llvm.initialize() -llvm.initialize_native_target() -llvm.initialize_native_asmprinter() -target = llvm.Target.from_default_triple() -targetMachine = target.create_target_machine() -backing_mod = llvm.parse_assembly("") -engine = llvm.create_mcjit_compiler(backing_mod, targetMachine) - - -def getKernelFunction(module): - for f in module.functions: - if not f.is_declaration: - return f - return None - - -def getNumRequiredQubits(function): - for a in function.attributes: - if "requiredQubits" in str(a): - return int( - str(a).split("requiredQubits\"=")[-1].split(" ")[0].replace( - "\"", "")) - - -# Here we test that the login endpoint works -@app.post("/login") -async def login(token: Union[str, None] = Header(alias="Authorization", - default=None)): - if 'token' == None: - raise HTTPException(status_code(401), detail="Credentials not provided") - return {"id_token": "hello", "refresh_token": "refreshToken"} - - -# Here we expose a way to post jobs, -# Must have a Access Token, Job Program must be Adaptive Profile -# with entry_point tag -@app.post("/job") -async def postJob(job: Job, - token: Union[str, None] = Header(alias="Authorization", - default=None)): - global createdJobs, shots - - if 'token' == None: - raise HTTPException(status_code(401), detail="Credentials not provided") - - print('Posting job with name = ', job.name, job.count) - name = job.name - newId = str(uuid.uuid4()) - program = job.program - decoded = base64.b64decode(program) - m = llvm.module.parse_bitcode(decoded) - mstr = str(m) - assert ('entry_point' in mstr) - - # Get the function, number of qubits, and kernel name - function = getKernelFunction(m) - if function == None: - raise Exception("Could not find kernel function") - numQubitsRequired = getNumRequiredQubits(function) - kernelFunctionName = function.name - - print("Kernel name = ", kernelFunctionName) - print("Requires {} qubits".format(numQubitsRequired)) - - # JIT Compile and get Function Pointer - engine.add_module(m) - engine.finalize_object() - engine.run_static_constructors() - funcPtr = engine.get_function_address(kernelFunctionName) - kernel = ctypes.CFUNCTYPE(None)(funcPtr) - - # Invoke the Kernel - cudaq.testing.toggleDynamicQubitManagement() - qubits, context = cudaq.testing.initialize(numQubitsRequired, job.count) - kernel() - results = cudaq.testing.finalize(qubits, context) - results.dump() - createdJobs[newId] = (name, results) - - engine.remove_module(m) - - # Job "created", return the id - return ({"job_token": newId}, 201) - - -# Retrieve the job, simulate having to wait by counting to 3 -# until we return the job results -@app.get("/job/{jobId}") -async def getJob(jobId: str): - global countJobGetRequests, createdJobs, shots - - # Simulate asynchronous execution - if countJobGetRequests < 3: - countJobGetRequests += 1 - return ({"status": "executing"}, 201) - - countJobGetRequests = 0 - name, counts = createdJobs[jobId] - retData = [] - for bits, count in counts.items(): - retData += [bits] * count - - # The simulators don't implement result recording features yet, so we have - # to mark these results specially (MOCK_SERVER_RESULTS) in order to allow - # downstream code to recognize that this isn't from a true QuEra QPU. - res = ({"status": "done", "results": {"MOCK_SERVER_RESULTS": retData}}, 201) - return res - - -def startServer(port): - print("Server Started") - uvicorn.run(app, port=port, host='0.0.0.0', log_level="info") - - -if __name__ == '__main__': - print("Server Starting") - startServer(62444)