Repository: NVIDIA/cuda-quantum
Pull request #2878: [docs] Add docs for adding a new backend
Author: login: nvidia-dobri
Labels: name: documentation
description: Improvements or additions to documentation
Created: 2025-05-07T15:38:58Z | Merged: 2025-05-17T00:12:48Z

Documentation for adding a new backend to CUDA-Q. Currently this only applies to a gate-based backend. Analog documentation to follow.


Diff:
diff --git a/docs/sphinx/using/backends/backends.rst b/docs/sphinx/using/backends/backends.rst
index aa0cbfcf17c..4c9b68d0e20 100644
--- a/docs/sphinx/using/backends/backends.rst
+++ b/docs/sphinx/using/backends/backends.rst
@@ -8,6 +8,8 @@ The CUDA-Q platform is a powerful tool with many different backends for running
 
 The figure below groups the backends into four categories, and described the general purpose for each.  See the following sections for a breakdown of the backends included in each section.
 
+To add a new hardware backend, please see :doc:`../extending/backend`
+
 .. image:: backends.png
    :width: 1000
 
diff --git a/docs/sphinx/using/extending/backend.rst b/docs/sphinx/using/extending/backend.rst
new file mode 100644
index 00000000000..b48bcf0a9b9
--- /dev/null
+++ b/docs/sphinx/using/extending/backend.rst
@@ -0,0 +1,623 @@
+********************************************
+Extending CUDA-Q with a new Hardware Backend
+********************************************
+
+This guide explains how to integrate a new quantum hardware provider with CUDA-Q. The integration process involves creating a server helper that handles communication with the provider's API, defining configuration files, and implementing necessary tests.
+
+Overview
+========
+
+CUDA-Q supports various quantum hardware :doc:`backends <../backends/hardware>` through a REST-based architecture. To add a new backend, you'll need to:
+
+1. Create a server helper class that handles API communication
+2. Define configuration files for the target
+3. Implement tests to verify functionality
+4. Add documentation for users
+
+The following sections detail each step of this process.
+
+Server Helper Implementation
+============================
+
+The server helper is the core component that handles communication with the quantum hardware provider's API. It extends the ``ServerHelper`` base class and implements methods for job submission, result retrieval, and other provider-specific functionality. The base class definition can be found in the `CUDA-Q repository <https://github.com/NVIDIA/cuda-quantum/blob/main/runtime/common/ServerHelper.h>`_.
+
+Directory Structure
+-------------------
+
+Create the following directory structure for your new backend (replace ``<provider_name>`` with your provider's name):
+
+.. code-block:: text
+
+    runtime/cudaq/platform/default/rest/helpers/<provider_name>/
+    ├── CMakeLists.txt
+    ├── ProviderNameServerHelper.cpp
+    └── <provider_name>.yml
+
+Server Helper Class
+-------------------
+
+Here's a template for implementing a server helper class:
+
+.. code-block:: cpp
+
+    // ProviderNameServerHelper.cpp
+    #include "common/Logger.h"
+    #include "common/RestClient.h"
+    #include "common/ServerHelper.h"
+    #include "cudaq/Support/Version.h"
+    #include "cudaq/utils/cudaq_utils.h"
+    #include <bitset>
+    #include <fstream>
+    #include <iostream>
+    #include <map>
+    #include <regex>
+    #include <sstream>
+    #include <thread>
+    #include <unordered_set>
+    
+    using json = nlohmann::json;
+    
+    namespace cudaq {
+    
+    /// @brief The ProviderNameServerHelper class extends the ServerHelper class
+    /// to handle interactions with the Provider Name server for submitting and
+    /// retrieving quantum computation jobs.
+    class ProviderNameServerHelper : public ServerHelper {
+      static constexpr const char *DEFAULT_URL = "https://api.provider-name.com";
+      static constexpr const char *DEFAULT_VERSION = "v1.0";
+    
+    public:
+      const std::string name() const override { return "<provider_name>"; }
+    
+      /// @brief Example implementation of authentication headers.
+      RestHeaders getHeaders() override {
+        RestHeaders headers;
+        headers["Content-Type"] = "application/json";
+        
+        // Add authentication headers if needed
+        if (backendConfig.count("api_key"))
+          headers["Authorization"] = "Bearer " + backendConfig["api_key"];
+        
+        return headers;
+      }
+    
+      /// @brief Example implementation of backend initialization.
+      void initialize(BackendConfig config) override {
+        cudaq::info("Initializing Provider Name Backend");
+        backendConfig = config;
+        
+        if (!backendConfig.count("url"))
+          backendConfig["url"] = DEFAULT_URL;
+        if (!backendConfig.count("version"))
+          backendConfig["version"] = DEFAULT_VERSION;
+
+        // Set shots if provided
+        if (config.find("shots") != config.end())
+          this->setShots(std::stoul(config["shots"]));
+      }
+    
+      /// @brief Example implementation of simple job creation.
+      ServerJobPayload createJob(std::vector<KernelExecution> &circuitCodes) override {
+        ServerMessage job;
+        job["content"] = circuitCodes[0].code;
+        job["shots"] = shots;
+        
+        RestHeaders headers = getHeaders();
+        std::string path = "/jobs";
+        
+        return std::make_tuple(backendConfig["url"] + path, headers, 
+                              std::vector<ServerMessage>{job});
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string extractJobId(ServerMessage &postResponse) override {
+        if (!postResponse.contains("id"))
+          return "";
+        
+        return postResponse.at("id");
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string constructGetJobPath(ServerMessage &postResponse) override {
+        return extractJobId(postResponse);
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string constructGetJobPath(std::string &jobId) override {
+        return backendConfig["url"] + "/jobs/" + jobId;
+      }
+    
+      /// @brief Example implementation of job status checking.
+      bool jobIsDone(ServerMessage &getJobResponse) override {
+        if (!getJobResponse.contains("status"))
+          return false;
+        
+        std::string status = getJobResponse["status"];
+        return status == "COMPLETED" || status == "FAILED";
+      }
+    
+      /// @brief Example implementation of result processing.
+      ///
+      /// The raw results from quantum hardware often need post-processing (bit
+      /// reordering, normalization, etc.) to match CUDA-Q's expectations.
+      /// This is the place to do that.
+      cudaq::sample_result processResults(ServerMessage &getJobResponse,
+                                         std::string &jobId) override {
+        cudaq::info("Processing results: {}", getJobResponse.dump());
+        
+        // Extract measurement results from the response
+        auto samplesJson = getJobResponse["results"]["counts"];
+        cudaq::CountsDictionary counts;
+        
+        for (auto &item : samplesJson.items()) {
+          std::string bitstring = item.key();
+          std::size_t count = item.value();
+          counts[bitstring] = count;
+        }
+        
+        // Create an ExecutionResult
+        cudaq::ExecutionResult execResult{counts};
+        
+        // Return the sample_result
+        return cudaq::sample_result{execResult};
+      }
+    
+      /// @brief Example implementation of polling configuration.
+      std::chrono::microseconds
+      nextResultPollingInterval(ServerMessage &postResponse) override {
+        return std::chrono::seconds(5);
+      }
+    };
+    
+    } // namespace cudaq
+    
+    // Register the server helper in the CUDA-Q server helper factory
+    CUDAQ_REGISTER_TYPE(cudaq::ServerHelper, cudaq::ProviderNameServerHelper, <provider_name>)
+
+``CMakeLists.txt``
+------------------
+
+You will need to configure CUDA-Q's ``cmake`` system for your new server helper. By convention, you should setup your target as optional by adding a CMake flag in the ``CMakeLists.txt`` at the root of the CUDA-Q repository:
+
+.. code-block:: cmake
+
+    # Enable <provider_name> target by default
+    if (NOT DEFINED CUDAQ_ENABLE_PROVIDER_NAME_BACKEND)
+      set(CUDAQ_ENABLE_PROVIDER_NAME_BACKEND ON CACHE BOOL "Enable building the <Provider Name> target.")
+    endif()
+
+Then, create a ``CMakeLists.txt`` file in your server helper's directory and check for this flag:
+
+.. code-block:: cmake
+
+    if(CUDAQ_ENABLE_PROVIDER_NAME_BACKEND)
+      target_sources(cudaq-rest-qpu PRIVATE ProviderNameServerHelper.cpp)
+      add_target_config(<provider_name>)
+      
+      add_library(cudaq-serverhelper-<provider_name> SHARED ProviderNameServerHelper.cpp)
+      target_link_libraries(cudaq-serverhelper-<provider_name>
+        PUBLIC
+        cudaq-common
+        fmt::fmt-header-only
+      )
+      install(TARGETS cudaq-serverhelper-<provider_name> DESTINATION lib)
+    endif()
+
+Target Configuration
+====================
+
+Create a ``YAML`` configuration file for your target:
+
+.. code-block:: yaml
+
+    # <provider_name>.yml
+    name: "<provider_name>"
+    description: "CUDA-Q target for Provider Name."
+    
+    config:
+      # Tell DefaultQuantumPlatform what QPU subtype to use
+      platform-qpu: remote_rest
+      # Add the rest-qpu library to the link list
+      link-libs: ["-lcudaq-rest-qpu"]
+      # Tell NVQ++ to generate glue code to set the target backend name
+      gen-target-backend: true
+      # Add preprocessor defines to compilation
+      preprocessor-defines: ["-D CUDAQ_QUANTUM_DEVICE"]
+      # Define the lowering pipeline
+      # This will cover applying hardware-specific constraints since each provider may have different native gate sets, requiring custom mappings and decompositions. You may need assistance from the CUDA-Q team to set this up correctly.
+      platform-lowering-config: "classical-optimization-pipeline,globalize-array-values,func.func(state-prep),unitary-synthesis,canonicalize,apply-op-specialization,aggressive-early-inlining,classical-optimization-pipeline,func.func(lower-to-cfg,canonicalize,multicontrol-decomposition),decomposition{enable-patterns=SToR1,TToR1,R1ToU3,U3ToRotations,CHToCX,CCZToCX,CRzToCX,CRyToCX,CRxToCX,CR1ToCX},quake-to-cc-prep,func.func(expand-control-veqs,combine-quantum-alloc,canonicalize,combine-measurements),symbol-dce"
+      # Tell the rest-qpu that we are generating QIR base profile.
+      # As of the time of this writing, qasm2, qir-base and qir-adaptive are supported.
+      codegen-emission: qir-base
+      # Library mode is only for simulators, physical backends must turn this off
+      library-mode: false
+    
+    # Some examples of target arguments are shown below.
+    # You do not need to add any arguments for your backend if you do not need them.
+    target-arguments:
+      - key: api-key
+        required: true
+        type: string
+        platform-arg: api_key
+        help-string: "API key for Provider Name."
+      - key: url
+        required: false
+        type: string
+        platform-arg: url
+        help-string: "Specify Provider Name API server URL."
+      - key: device
+        required: false
+        type: string
+        platform-arg: device
+        help-string: "Specify the Provider Name quantum device to use."
+
+Update Parent ``CMakeLists.txt``
+--------------------------------
+
+Add your provider to the parent ``CMakeLists.txt`` file:
+
+.. code-block:: cmake
+
+    # runtime/cudaq/platform/default/rest/helpers/CMakeLists.txt
+    add_subdirectory(<provider_name>)
+    add_subdirectory(ionq)
+    add_subdirectory(iqm)
+    # ... other providers
+
+Testing
+=======
+
+Unit Tests
+----------
+
+Create unit tests for your server helper:
+
+1. Create a directory structure:
+
+.. code-block:: text
+
+    unittests/backends/<provider_name>/
+    ├── CMakeLists.txt
+    ├── ProviderNameStartServerAndTest.sh.in
+    └── ProviderNameTester.cpp
+
+2. Implement the test files:
+
+.. code-block:: cmake
+
+    # CMakeLists.txt
+    add_executable(ProviderNameTester ProviderNameTester.cpp)
+    target_link_libraries(ProviderNameTester
+      PRIVATE
+      cudaq-common
+      cudaq
+      gtest_main
+    )
+    
+    configure_file(${CMAKE_CURRENT_SOURCE_DIR}/ProviderNameStartServerAndTest.sh.in
+                  ${CMAKE_CURRENT_BINARY_DIR}/ProviderNameStartServerAndTest.sh @ONLY)
+    
+    add_test(NAME ProviderNameTester COMMAND ${CMAKE_CURRENT_BINARY_DIR}/ProviderNameStartServerAndTest.sh)
+    set_tests_properties(ProviderNameTester PROPERTIES TIMEOUT 120)
+
+3. Create a shell script to start the mock server and run tests:
+
+.. code-block:: bash
+
+    #!/bin/bash
+    
+    # Start the mock server
+    python3 -m utils.mock_qpu.<provider_name> @PORT@ &
+    SERVER_PID=$!
+    
+    # Wait for server to start
+    sleep 2
+    
+    # Run the test
+    @CMAKE_CURRENT_BINARY_DIR@/ProviderNameTester
+    TEST_STATUS=$?
+    
+    # Kill the server
+    kill $SERVER_PID
+    
+    # Return the test status
+    exit $TEST_STATUS
+
+4. Implement the C++ test:
+
+.. code-block:: cpp
+
+    // ProviderNameTester.cpp
+    #include "common/Logger.h"
+    #include "common/RestClient.h"
+    #include "common/ServerHelper.h"
+    #include "cudaq/platform/quantum_platform.h"
+    #include "gtest/gtest.h"
+    
+    TEST(ProviderNameTester, checkSimpleCircuit) {
+      // Initialize the platform
+      auto platform = cudaq::get_platform();
+      platform->setTargetBackend("<provider_name>");
+      
+      // Set configuration
+      platform->setBackendParameter("url", "http://localhost:PORT");
+      platform->setBackendParameter("api_key", "test_key");
+      
+      // Create a simple circuit
+      auto kernel = cudaq::make_kernel();
+      auto qubits = kernel.qalloc(2);
+      kernel.h(qubits[0]);
+      kernel.cx(qubits[0], qubits[1]);
+      kernel.mz(qubits);
+      
+      // Execute the circuit
+      auto counts = cudaq::sample(kernel);
+      
+      // Check results
+      EXPECT_EQ(counts.size(), 2);
+      EXPECT_TRUE(counts.has_key("00"));
+      EXPECT_TRUE(counts.has_key("11"));
+    }
+
+To make sure the C++ tests don't run if your target is not enabled, add the following to ``targettests/lit.site.cfg.py.in``:
+
+.. code-block:: python
+
+    config.cudaq_backends_provider = "@CUDAQ_ENABLE_PROVIDER_NAME_BACKEND@"
+    if cmake_boolvar_to_bool(config.cudaq_backends_provider):
+        config.available_features.add('provider')
+        config.substitutions.append(('%provider_avail', 'true'))
+    else:
+        config.substitutions.append(('%provider_avail', 'false'))
+
+And add the following to your ``targettests`` ``.cpp`` file:
+
+.. code-block:: cpp
+
+    // RUN: if %provider_avail; then nvq++ %cpp_std --target provider %s -o %t.x; fi
+
+Mock Server
+-----------
+
+Create a mock server for testing:
+
+.. code-block:: text
+
+    utils/mock_qpu/<provider_name>/
+    └── __init__.py
+
+Implement the mock server:
+
+.. code-block:: python
+
+    # __init__.py
+    from http.server import BaseHTTPRequestHandler, HTTPServer
+    import json
+    import sys
+    import time
+    
+    class ProviderNameMockServer(BaseHTTPRequestHandler):
+        def _set_headers(self, status_code=200):
+            self.send_response(status_code)
+            self.send_header('Content-type', 'application/json')
+            self.end_headers()
+    
+        def do_POST(self):
+            content_length = int(self.headers['Content-Length'])
+            post_data = self.rfile.read(content_length)
+            data = json.loads(post_data.decode('utf-8'))
+            
+            if self.path == '/jobs':
+                # Create a job
+                response = {
+                    'id': 'job-123',
+                    'status': 'QUEUED'
+                }
+                self._set_headers()
+                self.wfile.write(json.dumps(response).encode())
+            else:
+                self._set_headers(404)
+                self.wfile.write(json.dumps({'error': 'Not found'}).encode())
+    
+        def do_GET(self):
+            if self.path.startswith('/jobs/job-123'):
+                # Return job status and results
+                response = {
+                    'id': 'job-123',
+                    'status': 'COMPLETED',
+                    'results': {
+                        'counts': {
+                            '00': 500,
+                            '11': 500
+                        }
+                    }
+                }
+                self._set_headers()
+                self.wfile.write(json.dumps(response).encode())
+            else:
+                self._set_headers(404)
+                self.wfile.write(json.dumps({'error': 'Not found'}).encode())
+    
+    def startServer(port=8000):
+        server_address = ('', port)
+        httpd = HTTPServer(server_address, ProviderNameMockServer)
+        print(f'Starting mock server on port {port}...')
+        httpd.serve_forever()
+    
+    if __name__ == '__main__':
+        port = int(sys.argv[1]) if len(sys.argv) > 1 else 8000
+        startServer(port)
+
+Python Tests
+------------
+
+Create Python tests for your backend:
+
+.. code-block:: python
+
+    # python/tests/backends/test_<provider_name>.py
+    import os
+    import sys
+    import time
+    import pytest
+    from multiprocessing import Process
+    
+    import cudaq
+    from cudaq import spin
+
+    skipIf<provider_name>NotInstalled = pytest.mark.skipif(
+        not (cudaq.has_target("<provider_name>")),
+        reason='Could not find `<provider_name>` in installation')
+    
+    try:
+        from utils.mock_qpu.<provider_name> import startServer
+    except:
+        print("Mock qpu not available, skipping Provider Name tests.")
+        pytest.skip("Mock qpu not available.", allow_module_level=True)
+    
+    # Define the port for the mock server - make sure this is unique
+    # across all tests.
+    port = 62444
+    
+    @pytest.fixture(scope="session", autouse=True)
+    def startUpMockServer():
+        # Set the targeted QPU
+        cudaq.set_target('<provider_name>',
+                        url=f'http://localhost:{port}',
+                        api_key="test_key")
+        
+        # Launch the Mock Server
+        p = Process(target=startServer, args=(port,))
+        p.start()
+        time.sleep(1)
+        
+        yield "Running the tests."
+        
+        # Kill the server
+        p.terminate()
+    
+    def test_<provider_name>_sample():
+        # Create the kernel
+        kernel = cudaq.make_kernel()
+        qubits = kernel.qalloc(2)
+        kernel.h(qubits[0])
+        kernel.cx(qubits[0], qubits[1])
+        kernel.mz(qubits)
+        
+        # Run sample
+        counts = cudaq.sample(kernel)
+        assert len(counts) == 2
+        assert '00' in counts
+        assert '11' in counts
+        
+        # Run sample asynchronously
+        future = cudaq.sample_async(kernel)
+        counts = future.get()
+        assert len(counts) == 2
+        assert '00' in counts
+        assert '11' in counts
+
+Integration Tests
+-----------------
+
+To ensure proper execution on the hardware, a validation backend must be provided that:
+
+1. Consumes the same format that your target will use
+2. Validates that circuits passing this validation will execute successfully on the actual hardware
+3. Can be accessed by CUDA-Q's GitHub CI/CD pipelines
+
+Your validation backend doesn't need to be publicly available, but it should:
+
+- Accept the same input format as your actual quantum processor
+- Return meaningful error messages for invalid circuits
+- Provide an API endpoint that can be called from our integration tests
+
+If your validation backend is not publicly available, please coordinate the exchange of necessary credentials for CI/CD with the CUDA-Q team.
+
+Add your target to ``.github/workflows/integration_tests.yml``:
+
+.. code-block:: yaml
+
+    - name: Submit to <provider_name> test server
+      if: (success() || failure()) && (inputs.target == '<provider_name>' || github.event_name == 'schedule')
+      run: |
+        echo "### Submit to <provider_name> server" >> $GITHUB_STEP_SUMMARY
+        # Set up any required dependencies        
+        # Set up environment variables for authentication
+        export PROVIDER_API_KEY='${{ secrets.PROVIDER_API_KEY }}'
+        
+        # Run the integration tests
+        python_tests="docs/sphinx/targets/python/<provider_name>.py"
+        cpp_tests="docs/sphinx/targets/cpp/<provider_name>.cpp"
+        
+        # Execute tests (see other provider examples for implementation details)
+        # ...
+
+Documentation
+=============
+
+Add documentation for your backend in the appropriate sections of the CUDA-Q documentation. This should include:
+
+1. How to access your server (authentication set up, documentation etc.)
+2. How to configure and use the backend
+3. Any provider-specific parameters or features
+4. Examples of running circuits on the backend
+5. Adding your logo to the diagram on :doc:`../backends/hardware`
+
+More specifically, you will need to modify at least the following files:
+
+* ``docs/sphinx/using/examples/hardware_providers.rst``
+* ``docs/sphinx/using/backends/hardware.rst``
+* ``docs/sphinx/using/backends/hardware/<your-technology>.rst``
+* ``docs/sphinx/targets/python/<provider_name>.py``
+* ``docs/sphinx/targets/cpp/<provider_name>.cpp``
+
+Example Usage
+=============
+
+Once your backend is implemented, users can use it as follows:
+
+.. code-block:: python
+
+    import cudaq
+    
+    # Set the target to your provider
+    cudaq.set_target('<provider_name>', 
+                    api_key='your_api_key',
+                    device='your_device')
+    
+    # Create and run a circuit
+    @cudaq.kernel
+    def bell():
+        qubits = cudaq.qvector(2)
+        h(qubits[0])
+        x.ctrl(qubits[0], qubits[1])
+        mz(qubits)
+    
+    # Run the circuit
+    counts = cudaq.sample(bell)
+    print(counts)
+
+Code Review
+===========
+
+Once you have implemented a ``ProviderNameServerHelper``, some basic tests, and documentation, please `create a PR <https://github.com/NVIDIA/cuda-quantum/pulls>`_ with your changes and tag the CUDA-Q team for review.
+
+Maintaining a Backend
+=====================
+
+Once your backend is integrated, you will need to maintain it. This includes:
+
+* Fixing bugs (in your integration, tests, or documentation)
+* Adding new features
+* Integrating with new CUDA-Q features (if additional integration is needed to use them)
+
+This is where having extensive tests against real hardware comes in handy. The benefits are two-fold:
+
+* It allows the CUDA-Q team to roll out new features without breaking your backend integration
+* It allows you to validate compatibility with CUDA-Q before rolling out a new version of your backend
+
+Conclusion
+==========
+
+By following this guide, you can integrate a new quantum hardware provider with CUDA-Q. The integration involves creating a server helper, defining configuration files, implementing tests, adding documentation, and going through a code review process. Once integrated, users can seamlessly run quantum circuits on your provider's hardware using the CUDA-Q framework.
diff --git a/docs/sphinx/using/extending/extending.rst b/docs/sphinx/using/extending/extending.rst
index fd755f79697..2841b4cb833 100644
--- a/docs/sphinx/using/extending/extending.rst
+++ b/docs/sphinx/using/extending/extending.rst
@@ -4,6 +4,7 @@ Extending CUDA-Q
 .. toctree::
   :maxdepth: 1
 
+  Add a new Hardware Backend <backend.rst>
   Create a new NVQIR Simulator <nvqir_simulator.rst>
   Working with CUDA-Q IR <cudaq_ir.rst>
   Create an MLIR Pass for CUDA-Q <mlir_pass.rst>


Reviews:
user: login: sacpis
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-07T18:25:25Z
user: login: sacpis
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-07T18:34:57Z
user: login: sacpis
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-07T18:35:39Z
user: login: nvidia-dobri
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-07T22:15:17Z
user: login: nvidia-dobri
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-07T22:15:33Z
user: login: khalatepradnya
body: Thanks @nvidia-dobri for getting this started!
A few additions for sake of completeness.
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-07T23:30:08Z
user: login: efratshabtai
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-12T16:25:23Z
user: login: mawolf2023
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-12T16:34:58Z
user: login: khalatepradnya
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-16T18:52:35Z
user: login: khalatepradnya
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-16T21:26:34Z
user: login: khalatepradnya
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-16T21:28:30Z
user: login: khalatepradnya
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-16T21:29:53Z
user: login: khalatepradnya
body: Minor edits recommended.
Thanks @nvidia-dobri !
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-05-16T21:31:16Z
user: login: nvidia-dobri
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-16T21:55:50Z
user: login: khalatepradnya
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-16T22:00:54Z

Review comments:
diff_hunk: @@ -0,0 +1,528 @@
+*************************
+Adding New Backends
+*************************
+
+This guide explains how to integrate a new quantum hardware provider with CUDA-Q. The integration process involves creating a server helper that handles communication with the provider's API, defining configuration files, and implementing necessary tests.
+
+Overview
+========
+
+CUDA-Q supports various quantum hardware providers through a REST-based architecture. To add a new backend, you'll need to:
+
+1. Create a server helper class that handles API communication
+2. Define configuration files for the target
+3. Implement tests to verify functionality
+4. Add documentation for users
+
+The following sections detail each step of this process.
+
+Server Helper Implementation
+===========================
+
+The server helper is the core component that handles communication with the quantum hardware provider's API. It extends the ``ServerHelper`` base class and implements methods for job submission, result retrieval, and other provider-specific functionality.
+
+Directory Structure
+------------------
+
+Create the following directory structure for your new backend (replace ``provider_name`` with your provider's name):
+
+.. code-block:: text
+
+    runtime/cudaq/platform/default/rest/helpers/provider_name/
+    ├── CMakeLists.txt
+    ├── ProviderNameServerHelper.cpp
+    └── provider_name.yml
path: docs/sphinx/using/backends/new_backends.rst
user: login: sacpis
body: Can we put a placeholder for `provider_name` (<provider_name>)?
created_at: 2025-05-07T18:25:24Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,528 @@
+*************************
+Adding New Backends
+*************************
+
+This guide explains how to integrate a new quantum hardware provider with CUDA-Q. The integration process involves creating a server helper that handles communication with the provider's API, defining configuration files, and implementing necessary tests.
+
+Overview
+========
+
+CUDA-Q supports various quantum hardware providers through a REST-based architecture. To add a new backend, you'll need to:
+
+1. Create a server helper class that handles API communication
+2. Define configuration files for the target
+3. Implement tests to verify functionality
+4. Add documentation for users
+
+The following sections detail each step of this process.
+
+Server Helper Implementation
+===========================
+
+The server helper is the core component that handles communication with the quantum hardware provider's API. It extends the ``ServerHelper`` base class and implements methods for job submission, result retrieval, and other provider-specific functionality.
+
+Directory Structure
+------------------
+
+Create the following directory structure for your new backend (replace ``provider_name`` with your provider's name):
+
+.. code-block:: text
+
+    runtime/cudaq/platform/default/rest/helpers/provider_name/
+    ├── CMakeLists.txt
+    ├── ProviderNameServerHelper.cpp
+    └── provider_name.yml
+
+Server Helper Class
+------------------
+
+Here's a template for implementing a server helper class:
+
+.. code-block:: cpp
+
+    // ProviderNameServerHelper.cpp
+    #include "common/Logger.h"
+    #include "common/RestClient.h"
+    #include "common/ServerHelper.h"
+    #include "cudaq/Support/Version.h"
+    #include "cudaq/utils/cudaq_utils.h"
+    #include <bitset>
+    #include <fstream>
+    #include <iostream>
+    #include <map>
+    #include <regex>
+    #include <sstream>
+    #include <thread>
+    #include <unordered_set>
+    
+    using json = nlohmann::json;
+    
+    namespace cudaq {
+    
+    /// @brief The ProviderNameServerHelper class extends the ServerHelper class
+    /// to handle interactions with the Provider Name server for submitting and
+    /// retrieving quantum computation jobs.
+    class ProviderNameServerHelper : public ServerHelper {
+      static constexpr const char *DEFAULT_URL = "https://api.provider-name.com";
+      static constexpr const char *DEFAULT_VERSION = "v1.0";
+    
+    public:
+      const std::string name() const override { return "provider_name"; }
+    
+      /// @brief Example implementation of authentication headers.
+      RestHeaders getHeaders() override {
+        RestHeaders headers;
+        headers["Content-Type"] = "application/json";
+        
+        // Add authentication headers if needed
+        if (backendConfig.count("api_key"))
+          headers["Authorization"] = "Bearer " + backendConfig["api_key"];
+        
+        return headers;
+      }
+    
+      /// @brief Example implementation of backend initialization.
+      void initialize(BackendConfig config) override {
+        cudaq::info("Initializing Provider Name Backend");
+        backendConfig = config;
+        
+        if (!backendConfig.count("url"))
+          backendConfig["url"] = DEFAULT_URL;
+        if (!backendConfig.count("version"))
+          backendConfig["version"] = DEFAULT_VERSION;
+
+          // Set shots if provided
+        if (config.find("shots") != config.end())
+          this->setShots(std::stoul(config["shots"]));
+      }
+    
+      /// @brief Example implementation of simple job creation.
+      ServerJobPayload createJob(std::vector<KernelExecution> &circuitCodes) override {
+        ServerMessage job;
+        job["content"] = circuitCodes[0].code;
+        job["shots"] = shots;
+        
+        RestHeaders headers = getHeaders();
+        std::string path = "/jobs";
+        
+        return std::make_tuple(backendConfig["url"] + path, headers, 
+                              std::vector<ServerMessage>{job});
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string extractJobId(ServerMessage &postResponse) override {
+        if (!postResponse.contains("id"))
+          return "";
+        
+        return postResponse.at("id");
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string constructGetJobPath(ServerMessage &postResponse) override {
+        return extractJobId(postResponse);
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string constructGetJobPath(std::string &jobId) override {
+        return backendConfig["url"] + "/jobs/" + jobId;
+      }
+    
+      /// @brief Example implementation of job status checking.
+      bool jobIsDone(ServerMessage &getJobResponse) override {
+        if (!getJobResponse.contains("status"))
+          return false;
+        
+        std::string status = getJobResponse["status"];
+        return status == "COMPLETED" || status == "FAILED";
+      }
+    
+      /// @brief Example implementation of result processing.
+      cudaq::sample_result processResults(ServerMessage &getJobResponse,
+                                         std::string &jobId) override {
+        cudaq::info("Processing results: {}", getJobResponse.dump());
+        
+        // Extract measurement results from the response
+        auto samplesJson = getJobResponse["results"]["counts"];
+        cudaq::CountsDictionary counts;
+        
+        for (auto &item : samplesJson.items()) {
+          std::string bitstring = item.key();
+          std::size_t count = item.value();
+          counts[bitstring] = count;
+        }
+        
+        // Create an ExecutionResult
+        cudaq::ExecutionResult execResult{counts};
+        
+        // Return the sample_result
+        return cudaq::sample_result{execResult};
+      }
+    
+      /// @brief Example implementation of polling configuration.
+      std::chrono::microseconds
+      nextResultPollingInterval(ServerMessage &postResponse) override {
+        return std::chrono::seconds(5);
+      }
+    };
+    
+    } // namespace cudaq
+    
+    // Register the server helper in the CUDA-Q server helper factory
+    CUDAQ_REGISTER_TYPE(cudaq::ServerHelper, cudaq::ProviderNameServerHelper, provider_name)
+
+CMakeLists.txt
+--------------
+
+Create a CMakeLists.txt file for your server helper:
+
+.. code-block:: cmake
+
+    # CMakeLists.txt
+    target_sources(cudaq-rest-qpu PRIVATE ProviderNameServerHelper.cpp)
+    add_target_config(provider_name)
+    
+    add_library(cudaq-serverhelper-provider_name SHARED ProviderNameServerHelper.cpp)
+    target_link_libraries(cudaq-serverhelper-provider_name
+      PUBLIC
+      cudaq-common
+      fmt::fmt-header-only
+    )
+    install(TARGETS cudaq-serverhelper-provider_name DESTINATION lib)
+
+Target Configuration
+===================
+
+Create a YAML configuration file for your target:
+
+.. code-block:: yaml
+
+    # provider_name.yml
+    name: "provider_name"
+    description: "CUDA-Q target for Provider Name."
+    
+    config:
+      # Tell DefaultQuantumPlatform what QPU subtype to use
+      platform-qpu: remote_rest
+      # Add the rest-qpu library to the link list
+      link-libs: ["-lcudaq-rest-qpu"]
+      # Tell NVQ++ to generate glue code to set the target backend name
+      gen-target-backend: true
+      # Add preprocessor defines to compilation
+      preprocessor-defines: ["-D CUDAQ_QUANTUM_DEVICE"]
+      # Define the lowering pipeline
+      # You will likely need assistance from the CUDA-Q team to set this up correctly
+      platform-lowering-config: "classical-optimization-pipeline,globalize-array-values,func.func(state-prep),unitary-synthesis,canonicalize,apply-op-specialization,aggressive-early-inlining,classical-optimization-pipeline,func.func(lower-to-cfg,canonicalize,multicontrol-decomposition),decomposition{enable-patterns=SToR1,TToR1,R1ToU3,U3ToRotations,CHToCX,CCZToCX,CRzToCX,CRyToCX,CRxToCX,CR1ToCX},quake-to-cc-prep,func.func(expand-control-veqs,combine-quantum-alloc,canonicalize,combine-measurements),symbol-dce"
+      # Tell the rest-qpu that we are generating OpenQASM 2.0.
+      # As of the time of this writing, qasm2, qir-base and qir-adaptive are supported.
+      codegen-emission: qasm2
+      # Library mode is only for simulators, physical backends must turn this off
+      library-mode: false
+    
+    # Some examples of target arguments are shown below.
+    # You do not need to add any arguments for your backend if you do not need them.
+    target-arguments:
+      - key: api-key
+        required: true
+        type: string
+        platform-arg: api_key
+        help-string: "API key for Provider Name."
+      - key: url
+        required: false
+        type: string
+        platform-arg: url
+        help-string: "Specify Provider Name API server URL."
+      - key: device
+        required: false
+        type: string
+        platform-arg: device
+        help-string: "Specify the Provider Name quantum device to use."
+
+Update Parent CMakeLists.txt
+----------------------------
+
+Add your provider to the parent CMakeLists.txt file:
+
+.. code-block:: cmake
+
+    # runtime/cudaq/platform/default/rest/helpers/CMakeLists.txt
+    add_subdirectory(provider_name)
+    add_subdirectory(ionq)
+    add_subdirectory(iqm)
+    # ... other providers
+
+Testing
+=======
+
+Unit Tests
+----------
+
+Create unit tests for your server helper:
+
+1. Create a directory structure:
+
+.. code-block:: text
+
+    unittests/backends/provider_name/
+    ├── CMakeLists.txt
+    ├── ProviderNameStartServerAndTest.sh.in
+    └── ProviderNameTester.cpp
+
+2. Implement the test files:
+
+.. code-block:: cmake
+
+    # CMakeLists.txt
+    add_executable(ProviderNameTester ProviderNameTester.cpp)
+    target_link_libraries(ProviderNameTester
+      PRIVATE
+      cudaq-common
+      cudaq
+      gtest_main
+    )
+    
+    configure_file(${CMAKE_CURRENT_SOURCE_DIR}/ProviderNameStartServerAndTest.sh.in
+                  ${CMAKE_CURRENT_BINARY_DIR}/ProviderNameStartServerAndTest.sh @ONLY)
+    
+    add_test(NAME ProviderNameTester COMMAND ${CMAKE_CURRENT_BINARY_DIR}/ProviderNameStartServerAndTest.sh)
+    set_tests_properties(ProviderNameTester PROPERTIES TIMEOUT 120)
+
+3. Create a shell script to start the mock server and run tests:
+
+.. code-block:: bash
+
+    #!/bin/bash
+    
+    # Start the mock server
+    python3 -m utils.mock_qpu.provider_name @PORT@ &
+    SERVER_PID=$!
+    
+    # Wait for server to start
+    sleep 2
+    
+    # Run the test
+    @CMAKE_CURRENT_BINARY_DIR@/ProviderNameTester
+    TEST_STATUS=$?
+    
+    # Kill the server
+    kill $SERVER_PID
+    
+    # Return the test status
+    exit $TEST_STATUS
+
+4. Implement the C++ test:
+
+.. code-block:: cpp
+
+    // ProviderNameTester.cpp
+    #include "common/Logger.h"
+    #include "common/RestClient.h"
+    #include "common/ServerHelper.h"
+    #include "cudaq/platform/quantum_platform.h"
+    #include "gtest/gtest.h"
+    
+    TEST(ProviderNameTester, checkSimpleCircuit) {
+      // Initialize the platform
+      auto platform = cudaq::get_platform();
+      platform->setTargetBackend("provider_name");
+      
+      // Set configuration
+      platform->setBackendParameter("url", "http://localhost:PORT");
+      platform->setBackendParameter("api_key", "test_key");
+      
+      // Create a simple circuit
+      auto kernel = cudaq::make_kernel();
+      auto qubits = kernel.qalloc(2);
+      kernel.h(qubits[0]);
+      kernel.cx(qubits[0], qubits[1]);
+      kernel.mz(qubits);
+      
+      // Execute the circuit
+      auto counts = cudaq::sample(kernel);
+      
+      // Check results
+      EXPECT_EQ(counts.size(), 2);
+      EXPECT_TRUE(counts.has_key("00"));
+      EXPECT_TRUE(counts.has_key("11"));
+    }
+
+Mock Server
+----------
+
+Create a mock server for testing:
+
+.. code-block:: text
+
+    utils/mock_qpu/provider_name/
+    └── __init__.py
+
+Implement the mock server:
+
+.. code-block:: python
+
+    # __init__.py
+    from http.server import BaseHTTPRequestHandler, HTTPServer
+    import json
+    import sys
+    import time
+    
+    class ProviderNameMockServer(BaseHTTPRequestHandler):
+        def _set_headers(self, status_code=200):
+            self.send_response(status_code)
+            self.send_header('Content-type', 'application/json')
+            self.end_headers()
+    
+        def do_POST(self):
+            content_length = int(self.headers['Content-Length'])
+            post_data = self.rfile.read(content_length)
+            data = json.loads(post_data.decode('utf-8'))
+            
+            if self.path == '/jobs':
+                # Create a job
+                response = {
+                    'id': 'job-123',
+                    'status': 'QUEUED'
+                }
+                self._set_headers()
+                self.wfile.write(json.dumps(response).encode())
+            else:
+                self._set_headers(404)
+                self.wfile.write(json.dumps({'error': 'Not found'}).encode())
+    
+        def do_GET(self):
+            if self.path.startswith('/jobs/job-123'):
+                # Return job status and results
+                response = {
+                    'id': 'job-123',
+                    'status': 'COMPLETED',
+                    'results': {
+                        'counts': {
+                            '00': 500,
+                            '11': 500
+                        }
+                    }
+                }
+                self._set_headers()
+                self.wfile.write(json.dumps(response).encode())
+            else:
+                self._set_headers(404)
+                self.wfile.write(json.dumps({'error': 'Not found'}).encode())
+    
+    def startServer(port=8000):
+        server_address = ('', port)
+        httpd = HTTPServer(server_address, ProviderNameMockServer)
+        print(f'Starting mock server on port {port}...')
+        httpd.serve_forever()
+    
+    if __name__ == '__main__':
+        port = int(sys.argv[1]) if len(sys.argv) > 1 else 8000
+        startServer(port)
path: docs/sphinx/using/backends/new_backends.rst
user: login: sacpis
body: Just a thought. Should we abstract most of the code from this file? So that users will have to write less code (related only to the provider).
created_at: 2025-05-07T18:34:57Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,528 @@
+*************************
+Adding New Backends
+*************************
+
+This guide explains how to integrate a new quantum hardware provider with CUDA-Q. The integration process involves creating a server helper that handles communication with the provider's API, defining configuration files, and implementing necessary tests.
+
+Overview
+========
+
+CUDA-Q supports various quantum hardware providers through a REST-based architecture. To add a new backend, you'll need to:
+
+1. Create a server helper class that handles API communication
+2. Define configuration files for the target
+3. Implement tests to verify functionality
+4. Add documentation for users
+
+The following sections detail each step of this process.
+
+Server Helper Implementation
+===========================
+
+The server helper is the core component that handles communication with the quantum hardware provider's API. It extends the ``ServerHelper`` base class and implements methods for job submission, result retrieval, and other provider-specific functionality.
+
+Directory Structure
+------------------
+
+Create the following directory structure for your new backend (replace ``provider_name`` with your provider's name):
+
+.. code-block:: text
+
+    runtime/cudaq/platform/default/rest/helpers/provider_name/
+    ├── CMakeLists.txt
+    ├── ProviderNameServerHelper.cpp
+    └── provider_name.yml
+
+Server Helper Class
+------------------
+
+Here's a template for implementing a server helper class:
+
+.. code-block:: cpp
+
+    // ProviderNameServerHelper.cpp
+    #include "common/Logger.h"
+    #include "common/RestClient.h"
+    #include "common/ServerHelper.h"
+    #include "cudaq/Support/Version.h"
+    #include "cudaq/utils/cudaq_utils.h"
+    #include <bitset>
+    #include <fstream>
+    #include <iostream>
+    #include <map>
+    #include <regex>
+    #include <sstream>
+    #include <thread>
+    #include <unordered_set>
+    
+    using json = nlohmann::json;
+    
+    namespace cudaq {
+    
+    /// @brief The ProviderNameServerHelper class extends the ServerHelper class
+    /// to handle interactions with the Provider Name server for submitting and
+    /// retrieving quantum computation jobs.
+    class ProviderNameServerHelper : public ServerHelper {
+      static constexpr const char *DEFAULT_URL = "https://api.provider-name.com";
+      static constexpr const char *DEFAULT_VERSION = "v1.0";
+    
+    public:
+      const std::string name() const override { return "provider_name"; }
+    
+      /// @brief Example implementation of authentication headers.
+      RestHeaders getHeaders() override {
+        RestHeaders headers;
+        headers["Content-Type"] = "application/json";
+        
+        // Add authentication headers if needed
+        if (backendConfig.count("api_key"))
+          headers["Authorization"] = "Bearer " + backendConfig["api_key"];
+        
+        return headers;
+      }
+    
+      /// @brief Example implementation of backend initialization.
+      void initialize(BackendConfig config) override {
+        cudaq::info("Initializing Provider Name Backend");
+        backendConfig = config;
+        
+        if (!backendConfig.count("url"))
+          backendConfig["url"] = DEFAULT_URL;
+        if (!backendConfig.count("version"))
+          backendConfig["version"] = DEFAULT_VERSION;
+
+          // Set shots if provided
+        if (config.find("shots") != config.end())
+          this->setShots(std::stoul(config["shots"]));
+      }
+    
+      /// @brief Example implementation of simple job creation.
+      ServerJobPayload createJob(std::vector<KernelExecution> &circuitCodes) override {
+        ServerMessage job;
+        job["content"] = circuitCodes[0].code;
+        job["shots"] = shots;
+        
+        RestHeaders headers = getHeaders();
+        std::string path = "/jobs";
+        
+        return std::make_tuple(backendConfig["url"] + path, headers, 
+                              std::vector<ServerMessage>{job});
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string extractJobId(ServerMessage &postResponse) override {
+        if (!postResponse.contains("id"))
+          return "";
+        
+        return postResponse.at("id");
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string constructGetJobPath(ServerMessage &postResponse) override {
+        return extractJobId(postResponse);
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string constructGetJobPath(std::string &jobId) override {
+        return backendConfig["url"] + "/jobs/" + jobId;
+      }
+    
+      /// @brief Example implementation of job status checking.
+      bool jobIsDone(ServerMessage &getJobResponse) override {
+        if (!getJobResponse.contains("status"))
+          return false;
+        
+        std::string status = getJobResponse["status"];
+        return status == "COMPLETED" || status == "FAILED";
+      }
+    
+      /// @brief Example implementation of result processing.
+      cudaq::sample_result processResults(ServerMessage &getJobResponse,
+                                         std::string &jobId) override {
+        cudaq::info("Processing results: {}", getJobResponse.dump());
+        
+        // Extract measurement results from the response
+        auto samplesJson = getJobResponse["results"]["counts"];
+        cudaq::CountsDictionary counts;
+        
+        for (auto &item : samplesJson.items()) {
+          std::string bitstring = item.key();
+          std::size_t count = item.value();
+          counts[bitstring] = count;
+        }
+        
+        // Create an ExecutionResult
+        cudaq::ExecutionResult execResult{counts};
+        
+        // Return the sample_result
+        return cudaq::sample_result{execResult};
+      }
+    
+      /// @brief Example implementation of polling configuration.
+      std::chrono::microseconds
+      nextResultPollingInterval(ServerMessage &postResponse) override {
+        return std::chrono::seconds(5);
+      }
+    };
+    
+    } // namespace cudaq
+    
+    // Register the server helper in the CUDA-Q server helper factory
+    CUDAQ_REGISTER_TYPE(cudaq::ServerHelper, cudaq::ProviderNameServerHelper, provider_name)
+
+CMakeLists.txt
+--------------
+
+Create a CMakeLists.txt file for your server helper:
+
+.. code-block:: cmake
+
+    # CMakeLists.txt
+    target_sources(cudaq-rest-qpu PRIVATE ProviderNameServerHelper.cpp)
+    add_target_config(provider_name)
+    
+    add_library(cudaq-serverhelper-provider_name SHARED ProviderNameServerHelper.cpp)
+    target_link_libraries(cudaq-serverhelper-provider_name
+      PUBLIC
+      cudaq-common
+      fmt::fmt-header-only
+    )
+    install(TARGETS cudaq-serverhelper-provider_name DESTINATION lib)
+
+Target Configuration
+===================
+
+Create a YAML configuration file for your target:
+
+.. code-block:: yaml
+
+    # provider_name.yml
+    name: "provider_name"
+    description: "CUDA-Q target for Provider Name."
+    
+    config:
+      # Tell DefaultQuantumPlatform what QPU subtype to use
+      platform-qpu: remote_rest
+      # Add the rest-qpu library to the link list
+      link-libs: ["-lcudaq-rest-qpu"]
+      # Tell NVQ++ to generate glue code to set the target backend name
+      gen-target-backend: true
+      # Add preprocessor defines to compilation
+      preprocessor-defines: ["-D CUDAQ_QUANTUM_DEVICE"]
+      # Define the lowering pipeline
+      # You will likely need assistance from the CUDA-Q team to set this up correctly
+      platform-lowering-config: "classical-optimization-pipeline,globalize-array-values,func.func(state-prep),unitary-synthesis,canonicalize,apply-op-specialization,aggressive-early-inlining,classical-optimization-pipeline,func.func(lower-to-cfg,canonicalize,multicontrol-decomposition),decomposition{enable-patterns=SToR1,TToR1,R1ToU3,U3ToRotations,CHToCX,CCZToCX,CRzToCX,CRyToCX,CRxToCX,CR1ToCX},quake-to-cc-prep,func.func(expand-control-veqs,combine-quantum-alloc,canonicalize,combine-measurements),symbol-dce"
+      # Tell the rest-qpu that we are generating OpenQASM 2.0.
+      # As of the time of this writing, qasm2, qir-base and qir-adaptive are supported.
+      codegen-emission: qasm2
+      # Library mode is only for simulators, physical backends must turn this off
+      library-mode: false
+    
+    # Some examples of target arguments are shown below.
+    # You do not need to add any arguments for your backend if you do not need them.
+    target-arguments:
+      - key: api-key
+        required: true
+        type: string
+        platform-arg: api_key
+        help-string: "API key for Provider Name."
+      - key: url
+        required: false
+        type: string
+        platform-arg: url
+        help-string: "Specify Provider Name API server URL."
+      - key: device
+        required: false
+        type: string
+        platform-arg: device
+        help-string: "Specify the Provider Name quantum device to use."
+
+Update Parent CMakeLists.txt
+----------------------------
+
+Add your provider to the parent CMakeLists.txt file:
+
+.. code-block:: cmake
+
+    # runtime/cudaq/platform/default/rest/helpers/CMakeLists.txt
+    add_subdirectory(provider_name)
+    add_subdirectory(ionq)
+    add_subdirectory(iqm)
+    # ... other providers
+
+Testing
+=======
+
+Unit Tests
+----------
+
+Create unit tests for your server helper:
+
+1. Create a directory structure:
+
+.. code-block:: text
+
+    unittests/backends/provider_name/
+    ├── CMakeLists.txt
+    ├── ProviderNameStartServerAndTest.sh.in
+    └── ProviderNameTester.cpp
+
+2. Implement the test files:
+
+.. code-block:: cmake
+
+    # CMakeLists.txt
+    add_executable(ProviderNameTester ProviderNameTester.cpp)
+    target_link_libraries(ProviderNameTester
+      PRIVATE
+      cudaq-common
+      cudaq
+      gtest_main
+    )
+    
+    configure_file(${CMAKE_CURRENT_SOURCE_DIR}/ProviderNameStartServerAndTest.sh.in
+                  ${CMAKE_CURRENT_BINARY_DIR}/ProviderNameStartServerAndTest.sh @ONLY)
+    
+    add_test(NAME ProviderNameTester COMMAND ${CMAKE_CURRENT_BINARY_DIR}/ProviderNameStartServerAndTest.sh)
+    set_tests_properties(ProviderNameTester PROPERTIES TIMEOUT 120)
+
+3. Create a shell script to start the mock server and run tests:
+
+.. code-block:: bash
+
+    #!/bin/bash
+    
+    # Start the mock server
+    python3 -m utils.mock_qpu.provider_name @PORT@ &
+    SERVER_PID=$!
+    
+    # Wait for server to start
+    sleep 2
+    
+    # Run the test
+    @CMAKE_CURRENT_BINARY_DIR@/ProviderNameTester
+    TEST_STATUS=$?
+    
+    # Kill the server
+    kill $SERVER_PID
+    
+    # Return the test status
+    exit $TEST_STATUS
+
+4. Implement the C++ test:
+
+.. code-block:: cpp
+
+    // ProviderNameTester.cpp
+    #include "common/Logger.h"
+    #include "common/RestClient.h"
+    #include "common/ServerHelper.h"
+    #include "cudaq/platform/quantum_platform.h"
+    #include "gtest/gtest.h"
+    
+    TEST(ProviderNameTester, checkSimpleCircuit) {
+      // Initialize the platform
+      auto platform = cudaq::get_platform();
+      platform->setTargetBackend("provider_name");
+      
+      // Set configuration
+      platform->setBackendParameter("url", "http://localhost:PORT");
+      platform->setBackendParameter("api_key", "test_key");
+      
+      // Create a simple circuit
+      auto kernel = cudaq::make_kernel();
+      auto qubits = kernel.qalloc(2);
+      kernel.h(qubits[0]);
+      kernel.cx(qubits[0], qubits[1]);
+      kernel.mz(qubits);
+      
+      // Execute the circuit
+      auto counts = cudaq::sample(kernel);
+      
+      // Check results
+      EXPECT_EQ(counts.size(), 2);
+      EXPECT_TRUE(counts.has_key("00"));
+      EXPECT_TRUE(counts.has_key("11"));
+    }
+
+Mock Server
+----------
+
+Create a mock server for testing:
+
+.. code-block:: text
+
+    utils/mock_qpu/provider_name/
+    └── __init__.py
+
+Implement the mock server:
+
+.. code-block:: python
+
+    # __init__.py
+    from http.server import BaseHTTPRequestHandler, HTTPServer
+    import json
+    import sys
+    import time
+    
+    class ProviderNameMockServer(BaseHTTPRequestHandler):
+        def _set_headers(self, status_code=200):
+            self.send_response(status_code)
+            self.send_header('Content-type', 'application/json')
+            self.end_headers()
+    
+        def do_POST(self):
+            content_length = int(self.headers['Content-Length'])
+            post_data = self.rfile.read(content_length)
+            data = json.loads(post_data.decode('utf-8'))
+            
+            if self.path == '/jobs':
+                # Create a job
+                response = {
+                    'id': 'job-123',
+                    'status': 'QUEUED'
+                }
+                self._set_headers()
+                self.wfile.write(json.dumps(response).encode())
+            else:
+                self._set_headers(404)
+                self.wfile.write(json.dumps({'error': 'Not found'}).encode())
+    
+        def do_GET(self):
+            if self.path.startswith('/jobs/job-123'):
+                # Return job status and results
+                response = {
+                    'id': 'job-123',
+                    'status': 'COMPLETED',
+                    'results': {
+                        'counts': {
+                            '00': 500,
+                            '11': 500
+                        }
+                    }
+                }
+                self._set_headers()
+                self.wfile.write(json.dumps(response).encode())
+            else:
+                self._set_headers(404)
+                self.wfile.write(json.dumps({'error': 'Not found'}).encode())
+    
+    def startServer(port=8000):
+        server_address = ('', port)
+        httpd = HTTPServer(server_address, ProviderNameMockServer)
+        print(f'Starting mock server on port {port}...')
+        httpd.serve_forever()
+    
+    if __name__ == '__main__':
+        port = int(sys.argv[1]) if len(sys.argv) > 1 else 8000
+        startServer(port)
+
+Python Tests
+-----------
+
+Create Python tests for your backend:
+
+.. code-block:: python
+
+    # python/tests/backends/test_provider_name.py
+    import os
+    import sys
+    import time
+    import pytest
+    from multiprocessing import Process
+    
+    import cudaq
+    from cudaq import spin
+    
+    try:
+        from utils.mock_qpu.provider_name import startServer
+    except:
+        print("Mock qpu not available, skipping Provider Name 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():
+        # Set the targeted QPU
+        cudaq.set_target('provider_name',
+                        url=f'http://localhost:{port}',
+                        api_key="test_key")
+        
+        # Launch the Mock Server
+        p = Process(target=startServer, args=(port,))
+        p.start()
+        time.sleep(1)
+        
+        yield "Running the tests."
+        
+        # Kill the server
+        p.terminate()
+    
+    def test_provider_name_sample():
+        # Create the kernel
+        kernel = cudaq.make_kernel()
+        qubits = kernel.qalloc(2)
+        kernel.h(qubits[0])
+        kernel.cx(qubits[0], qubits[1])
+        kernel.mz(qubits)
+        
+        # Run sample
+        counts = cudaq.sample(kernel)
+        assert len(counts) == 2
+        assert '00' in counts
+        assert '11' in counts
+        
+        # Run sample asynchronously
+        future = cudaq.sample_async(kernel)
+        counts = future.get()
+        assert len(counts) == 2
+        assert '00' in counts
+        assert '11' in counts
path: docs/sphinx/using/backends/new_backends.rst
user: login: sacpis
body: Same here.
created_at: 2025-05-07T18:35:39Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,528 @@
+*************************
+Adding New Backends
+*************************
+
+This guide explains how to integrate a new quantum hardware provider with CUDA-Q. The integration process involves creating a server helper that handles communication with the provider's API, defining configuration files, and implementing necessary tests.
+
+Overview
+========
+
+CUDA-Q supports various quantum hardware providers through a REST-based architecture. To add a new backend, you'll need to:
+
+1. Create a server helper class that handles API communication
+2. Define configuration files for the target
+3. Implement tests to verify functionality
+4. Add documentation for users
+
+The following sections detail each step of this process.
+
+Server Helper Implementation
+===========================
+
+The server helper is the core component that handles communication with the quantum hardware provider's API. It extends the ``ServerHelper`` base class and implements methods for job submission, result retrieval, and other provider-specific functionality.
+
+Directory Structure
+------------------
+
+Create the following directory structure for your new backend (replace ``provider_name`` with your provider's name):
+
+.. code-block:: text
+
+    runtime/cudaq/platform/default/rest/helpers/provider_name/
+    ├── CMakeLists.txt
+    ├── ProviderNameServerHelper.cpp
+    └── provider_name.yml
+
+Server Helper Class
+------------------
+
+Here's a template for implementing a server helper class:
+
+.. code-block:: cpp
+
+    // ProviderNameServerHelper.cpp
+    #include "common/Logger.h"
+    #include "common/RestClient.h"
+    #include "common/ServerHelper.h"
+    #include "cudaq/Support/Version.h"
+    #include "cudaq/utils/cudaq_utils.h"
+    #include <bitset>
+    #include <fstream>
+    #include <iostream>
+    #include <map>
+    #include <regex>
+    #include <sstream>
+    #include <thread>
+    #include <unordered_set>
+    
+    using json = nlohmann::json;
+    
+    namespace cudaq {
+    
+    /// @brief The ProviderNameServerHelper class extends the ServerHelper class
+    /// to handle interactions with the Provider Name server for submitting and
+    /// retrieving quantum computation jobs.
+    class ProviderNameServerHelper : public ServerHelper {
+      static constexpr const char *DEFAULT_URL = "https://api.provider-name.com";
+      static constexpr const char *DEFAULT_VERSION = "v1.0";
+    
+    public:
+      const std::string name() const override { return "provider_name"; }
+    
+      /// @brief Example implementation of authentication headers.
+      RestHeaders getHeaders() override {
+        RestHeaders headers;
+        headers["Content-Type"] = "application/json";
+        
+        // Add authentication headers if needed
+        if (backendConfig.count("api_key"))
+          headers["Authorization"] = "Bearer " + backendConfig["api_key"];
+        
+        return headers;
+      }
+    
+      /// @brief Example implementation of backend initialization.
+      void initialize(BackendConfig config) override {
+        cudaq::info("Initializing Provider Name Backend");
+        backendConfig = config;
+        
+        if (!backendConfig.count("url"))
+          backendConfig["url"] = DEFAULT_URL;
+        if (!backendConfig.count("version"))
+          backendConfig["version"] = DEFAULT_VERSION;
+
+          // Set shots if provided
+        if (config.find("shots") != config.end())
+          this->setShots(std::stoul(config["shots"]));
+      }
+    
+      /// @brief Example implementation of simple job creation.
+      ServerJobPayload createJob(std::vector<KernelExecution> &circuitCodes) override {
+        ServerMessage job;
+        job["content"] = circuitCodes[0].code;
+        job["shots"] = shots;
+        
+        RestHeaders headers = getHeaders();
+        std::string path = "/jobs";
+        
+        return std::make_tuple(backendConfig["url"] + path, headers, 
+                              std::vector<ServerMessage>{job});
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string extractJobId(ServerMessage &postResponse) override {
+        if (!postResponse.contains("id"))
+          return "";
+        
+        return postResponse.at("id");
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string constructGetJobPath(ServerMessage &postResponse) override {
+        return extractJobId(postResponse);
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string constructGetJobPath(std::string &jobId) override {
+        return backendConfig["url"] + "/jobs/" + jobId;
+      }
+    
+      /// @brief Example implementation of job status checking.
+      bool jobIsDone(ServerMessage &getJobResponse) override {
+        if (!getJobResponse.contains("status"))
+          return false;
+        
+        std::string status = getJobResponse["status"];
+        return status == "COMPLETED" || status == "FAILED";
+      }
+    
+      /// @brief Example implementation of result processing.
+      cudaq::sample_result processResults(ServerMessage &getJobResponse,
+                                         std::string &jobId) override {
+        cudaq::info("Processing results: {}", getJobResponse.dump());
+        
+        // Extract measurement results from the response
+        auto samplesJson = getJobResponse["results"]["counts"];
+        cudaq::CountsDictionary counts;
+        
+        for (auto &item : samplesJson.items()) {
+          std::string bitstring = item.key();
+          std::size_t count = item.value();
+          counts[bitstring] = count;
+        }
+        
+        // Create an ExecutionResult
+        cudaq::ExecutionResult execResult{counts};
+        
+        // Return the sample_result
+        return cudaq::sample_result{execResult};
+      }
+    
+      /// @brief Example implementation of polling configuration.
+      std::chrono::microseconds
+      nextResultPollingInterval(ServerMessage &postResponse) override {
+        return std::chrono::seconds(5);
+      }
+    };
+    
+    } // namespace cudaq
+    
+    // Register the server helper in the CUDA-Q server helper factory
+    CUDAQ_REGISTER_TYPE(cudaq::ServerHelper, cudaq::ProviderNameServerHelper, provider_name)
+
+CMakeLists.txt
+--------------
+
+Create a CMakeLists.txt file for your server helper:
+
+.. code-block:: cmake
+
+    # CMakeLists.txt
+    target_sources(cudaq-rest-qpu PRIVATE ProviderNameServerHelper.cpp)
+    add_target_config(provider_name)
+    
+    add_library(cudaq-serverhelper-provider_name SHARED ProviderNameServerHelper.cpp)
+    target_link_libraries(cudaq-serverhelper-provider_name
+      PUBLIC
+      cudaq-common
+      fmt::fmt-header-only
+    )
+    install(TARGETS cudaq-serverhelper-provider_name DESTINATION lib)
+
+Target Configuration
+===================
+
+Create a YAML configuration file for your target:
+
+.. code-block:: yaml
+
+    # provider_name.yml
+    name: "provider_name"
+    description: "CUDA-Q target for Provider Name."
+    
+    config:
+      # Tell DefaultQuantumPlatform what QPU subtype to use
+      platform-qpu: remote_rest
+      # Add the rest-qpu library to the link list
+      link-libs: ["-lcudaq-rest-qpu"]
+      # Tell NVQ++ to generate glue code to set the target backend name
+      gen-target-backend: true
+      # Add preprocessor defines to compilation
+      preprocessor-defines: ["-D CUDAQ_QUANTUM_DEVICE"]
+      # Define the lowering pipeline
+      # You will likely need assistance from the CUDA-Q team to set this up correctly
+      platform-lowering-config: "classical-optimization-pipeline,globalize-array-values,func.func(state-prep),unitary-synthesis,canonicalize,apply-op-specialization,aggressive-early-inlining,classical-optimization-pipeline,func.func(lower-to-cfg,canonicalize,multicontrol-decomposition),decomposition{enable-patterns=SToR1,TToR1,R1ToU3,U3ToRotations,CHToCX,CCZToCX,CRzToCX,CRyToCX,CRxToCX,CR1ToCX},quake-to-cc-prep,func.func(expand-control-veqs,combine-quantum-alloc,canonicalize,combine-measurements),symbol-dce"
+      # Tell the rest-qpu that we are generating OpenQASM 2.0.
+      # As of the time of this writing, qasm2, qir-base and qir-adaptive are supported.
+      codegen-emission: qasm2
+      # Library mode is only for simulators, physical backends must turn this off
+      library-mode: false
+    
+    # Some examples of target arguments are shown below.
+    # You do not need to add any arguments for your backend if you do not need them.
+    target-arguments:
+      - key: api-key
+        required: true
+        type: string
+        platform-arg: api_key
+        help-string: "API key for Provider Name."
+      - key: url
+        required: false
+        type: string
+        platform-arg: url
+        help-string: "Specify Provider Name API server URL."
+      - key: device
+        required: false
+        type: string
+        platform-arg: device
+        help-string: "Specify the Provider Name quantum device to use."
+
+Update Parent CMakeLists.txt
+----------------------------
+
+Add your provider to the parent CMakeLists.txt file:
+
+.. code-block:: cmake
+
+    # runtime/cudaq/platform/default/rest/helpers/CMakeLists.txt
+    add_subdirectory(provider_name)
+    add_subdirectory(ionq)
+    add_subdirectory(iqm)
+    # ... other providers
+
+Testing
+=======
+
+Unit Tests
+----------
+
+Create unit tests for your server helper:
+
+1. Create a directory structure:
+
+.. code-block:: text
+
+    unittests/backends/provider_name/
+    ├── CMakeLists.txt
+    ├── ProviderNameStartServerAndTest.sh.in
+    └── ProviderNameTester.cpp
+
+2. Implement the test files:
+
+.. code-block:: cmake
+
+    # CMakeLists.txt
+    add_executable(ProviderNameTester ProviderNameTester.cpp)
+    target_link_libraries(ProviderNameTester
+      PRIVATE
+      cudaq-common
+      cudaq
+      gtest_main
+    )
+    
+    configure_file(${CMAKE_CURRENT_SOURCE_DIR}/ProviderNameStartServerAndTest.sh.in
+                  ${CMAKE_CURRENT_BINARY_DIR}/ProviderNameStartServerAndTest.sh @ONLY)
+    
+    add_test(NAME ProviderNameTester COMMAND ${CMAKE_CURRENT_BINARY_DIR}/ProviderNameStartServerAndTest.sh)
+    set_tests_properties(ProviderNameTester PROPERTIES TIMEOUT 120)
+
+3. Create a shell script to start the mock server and run tests:
+
+.. code-block:: bash
+
+    #!/bin/bash
+    
+    # Start the mock server
+    python3 -m utils.mock_qpu.provider_name @PORT@ &
+    SERVER_PID=$!
+    
+    # Wait for server to start
+    sleep 2
+    
+    # Run the test
+    @CMAKE_CURRENT_BINARY_DIR@/ProviderNameTester
+    TEST_STATUS=$?
+    
+    # Kill the server
+    kill $SERVER_PID
+    
+    # Return the test status
+    exit $TEST_STATUS
+
+4. Implement the C++ test:
+
+.. code-block:: cpp
+
+    // ProviderNameTester.cpp
+    #include "common/Logger.h"
+    #include "common/RestClient.h"
+    #include "common/ServerHelper.h"
+    #include "cudaq/platform/quantum_platform.h"
+    #include "gtest/gtest.h"
+    
+    TEST(ProviderNameTester, checkSimpleCircuit) {
+      // Initialize the platform
+      auto platform = cudaq::get_platform();
+      platform->setTargetBackend("provider_name");
+      
+      // Set configuration
+      platform->setBackendParameter("url", "http://localhost:PORT");
+      platform->setBackendParameter("api_key", "test_key");
+      
+      // Create a simple circuit
+      auto kernel = cudaq::make_kernel();
+      auto qubits = kernel.qalloc(2);
+      kernel.h(qubits[0]);
+      kernel.cx(qubits[0], qubits[1]);
+      kernel.mz(qubits);
+      
+      // Execute the circuit
+      auto counts = cudaq::sample(kernel);
+      
+      // Check results
+      EXPECT_EQ(counts.size(), 2);
+      EXPECT_TRUE(counts.has_key("00"));
+      EXPECT_TRUE(counts.has_key("11"));
+    }
+
+Mock Server
+----------
+
+Create a mock server for testing:
+
+.. code-block:: text
+
+    utils/mock_qpu/provider_name/
+    └── __init__.py
+
+Implement the mock server:
+
+.. code-block:: python
+
+    # __init__.py
+    from http.server import BaseHTTPRequestHandler, HTTPServer
+    import json
+    import sys
+    import time
+    
+    class ProviderNameMockServer(BaseHTTPRequestHandler):
+        def _set_headers(self, status_code=200):
+            self.send_response(status_code)
+            self.send_header('Content-type', 'application/json')
+            self.end_headers()
+    
+        def do_POST(self):
+            content_length = int(self.headers['Content-Length'])
+            post_data = self.rfile.read(content_length)
+            data = json.loads(post_data.decode('utf-8'))
+            
+            if self.path == '/jobs':
+                # Create a job
+                response = {
+                    'id': 'job-123',
+                    'status': 'QUEUED'
+                }
+                self._set_headers()
+                self.wfile.write(json.dumps(response).encode())
+            else:
+                self._set_headers(404)
+                self.wfile.write(json.dumps({'error': 'Not found'}).encode())
+    
+        def do_GET(self):
+            if self.path.startswith('/jobs/job-123'):
+                # Return job status and results
+                response = {
+                    'id': 'job-123',
+                    'status': 'COMPLETED',
+                    'results': {
+                        'counts': {
+                            '00': 500,
+                            '11': 500
+                        }
+                    }
+                }
+                self._set_headers()
+                self.wfile.write(json.dumps(response).encode())
+            else:
+                self._set_headers(404)
+                self.wfile.write(json.dumps({'error': 'Not found'}).encode())
+    
+    def startServer(port=8000):
+        server_address = ('', port)
+        httpd = HTTPServer(server_address, ProviderNameMockServer)
+        print(f'Starting mock server on port {port}...')
+        httpd.serve_forever()
+    
+    if __name__ == '__main__':
+        port = int(sys.argv[1]) if len(sys.argv) > 1 else 8000
+        startServer(port)
path: docs/sphinx/using/backends/new_backends.rst
user: login: nvidia-dobri
body: We can... but I'd rather not scope creep on documenting the current process vs overhauling the process.
created_at: 2025-05-07T22:15:17Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,528 @@
+*************************
+Adding New Backends
+*************************
+
+This guide explains how to integrate a new quantum hardware provider with CUDA-Q. The integration process involves creating a server helper that handles communication with the provider's API, defining configuration files, and implementing necessary tests.
+
+Overview
+========
+
+CUDA-Q supports various quantum hardware providers through a REST-based architecture. To add a new backend, you'll need to:
+
+1. Create a server helper class that handles API communication
+2. Define configuration files for the target
+3. Implement tests to verify functionality
+4. Add documentation for users
+
+The following sections detail each step of this process.
+
+Server Helper Implementation
+===========================
+
+The server helper is the core component that handles communication with the quantum hardware provider's API. It extends the ``ServerHelper`` base class and implements methods for job submission, result retrieval, and other provider-specific functionality.
+
+Directory Structure
+------------------
+
+Create the following directory structure for your new backend (replace ``provider_name`` with your provider's name):
+
+.. code-block:: text
+
+    runtime/cudaq/platform/default/rest/helpers/provider_name/
+    ├── CMakeLists.txt
+    ├── ProviderNameServerHelper.cpp
+    └── provider_name.yml
+
+Server Helper Class
+------------------
+
+Here's a template for implementing a server helper class:
+
+.. code-block:: cpp
+
+    // ProviderNameServerHelper.cpp
+    #include "common/Logger.h"
+    #include "common/RestClient.h"
+    #include "common/ServerHelper.h"
+    #include "cudaq/Support/Version.h"
+    #include "cudaq/utils/cudaq_utils.h"
+    #include <bitset>
+    #include <fstream>
+    #include <iostream>
+    #include <map>
+    #include <regex>
+    #include <sstream>
+    #include <thread>
+    #include <unordered_set>
+    
+    using json = nlohmann::json;
+    
+    namespace cudaq {
+    
+    /// @brief The ProviderNameServerHelper class extends the ServerHelper class
+    /// to handle interactions with the Provider Name server for submitting and
+    /// retrieving quantum computation jobs.
+    class ProviderNameServerHelper : public ServerHelper {
+      static constexpr const char *DEFAULT_URL = "https://api.provider-name.com";
+      static constexpr const char *DEFAULT_VERSION = "v1.0";
+    
+    public:
+      const std::string name() const override { return "provider_name"; }
+    
+      /// @brief Example implementation of authentication headers.
+      RestHeaders getHeaders() override {
+        RestHeaders headers;
+        headers["Content-Type"] = "application/json";
+        
+        // Add authentication headers if needed
+        if (backendConfig.count("api_key"))
+          headers["Authorization"] = "Bearer " + backendConfig["api_key"];
+        
+        return headers;
+      }
+    
+      /// @brief Example implementation of backend initialization.
+      void initialize(BackendConfig config) override {
+        cudaq::info("Initializing Provider Name Backend");
+        backendConfig = config;
+        
+        if (!backendConfig.count("url"))
+          backendConfig["url"] = DEFAULT_URL;
+        if (!backendConfig.count("version"))
+          backendConfig["version"] = DEFAULT_VERSION;
+
+          // Set shots if provided
+        if (config.find("shots") != config.end())
+          this->setShots(std::stoul(config["shots"]));
+      }
+    
+      /// @brief Example implementation of simple job creation.
+      ServerJobPayload createJob(std::vector<KernelExecution> &circuitCodes) override {
+        ServerMessage job;
+        job["content"] = circuitCodes[0].code;
+        job["shots"] = shots;
+        
+        RestHeaders headers = getHeaders();
+        std::string path = "/jobs";
+        
+        return std::make_tuple(backendConfig["url"] + path, headers, 
+                              std::vector<ServerMessage>{job});
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string extractJobId(ServerMessage &postResponse) override {
+        if (!postResponse.contains("id"))
+          return "";
+        
+        return postResponse.at("id");
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string constructGetJobPath(ServerMessage &postResponse) override {
+        return extractJobId(postResponse);
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string constructGetJobPath(std::string &jobId) override {
+        return backendConfig["url"] + "/jobs/" + jobId;
+      }
+    
+      /// @brief Example implementation of job status checking.
+      bool jobIsDone(ServerMessage &getJobResponse) override {
+        if (!getJobResponse.contains("status"))
+          return false;
+        
+        std::string status = getJobResponse["status"];
+        return status == "COMPLETED" || status == "FAILED";
+      }
+    
+      /// @brief Example implementation of result processing.
+      cudaq::sample_result processResults(ServerMessage &getJobResponse,
+                                         std::string &jobId) override {
+        cudaq::info("Processing results: {}", getJobResponse.dump());
+        
+        // Extract measurement results from the response
+        auto samplesJson = getJobResponse["results"]["counts"];
+        cudaq::CountsDictionary counts;
+        
+        for (auto &item : samplesJson.items()) {
+          std::string bitstring = item.key();
+          std::size_t count = item.value();
+          counts[bitstring] = count;
+        }
+        
+        // Create an ExecutionResult
+        cudaq::ExecutionResult execResult{counts};
+        
+        // Return the sample_result
+        return cudaq::sample_result{execResult};
+      }
+    
+      /// @brief Example implementation of polling configuration.
+      std::chrono::microseconds
+      nextResultPollingInterval(ServerMessage &postResponse) override {
+        return std::chrono::seconds(5);
+      }
+    };
+    
+    } // namespace cudaq
+    
+    // Register the server helper in the CUDA-Q server helper factory
+    CUDAQ_REGISTER_TYPE(cudaq::ServerHelper, cudaq::ProviderNameServerHelper, provider_name)
+
+CMakeLists.txt
+--------------
+
+Create a CMakeLists.txt file for your server helper:
+
+.. code-block:: cmake
+
+    # CMakeLists.txt
+    target_sources(cudaq-rest-qpu PRIVATE ProviderNameServerHelper.cpp)
+    add_target_config(provider_name)
+    
+    add_library(cudaq-serverhelper-provider_name SHARED ProviderNameServerHelper.cpp)
+    target_link_libraries(cudaq-serverhelper-provider_name
+      PUBLIC
+      cudaq-common
+      fmt::fmt-header-only
+    )
+    install(TARGETS cudaq-serverhelper-provider_name DESTINATION lib)
+
+Target Configuration
+===================
+
+Create a YAML configuration file for your target:
+
+.. code-block:: yaml
+
+    # provider_name.yml
+    name: "provider_name"
+    description: "CUDA-Q target for Provider Name."
+    
+    config:
+      # Tell DefaultQuantumPlatform what QPU subtype to use
+      platform-qpu: remote_rest
+      # Add the rest-qpu library to the link list
+      link-libs: ["-lcudaq-rest-qpu"]
+      # Tell NVQ++ to generate glue code to set the target backend name
+      gen-target-backend: true
+      # Add preprocessor defines to compilation
+      preprocessor-defines: ["-D CUDAQ_QUANTUM_DEVICE"]
+      # Define the lowering pipeline
+      # You will likely need assistance from the CUDA-Q team to set this up correctly
+      platform-lowering-config: "classical-optimization-pipeline,globalize-array-values,func.func(state-prep),unitary-synthesis,canonicalize,apply-op-specialization,aggressive-early-inlining,classical-optimization-pipeline,func.func(lower-to-cfg,canonicalize,multicontrol-decomposition),decomposition{enable-patterns=SToR1,TToR1,R1ToU3,U3ToRotations,CHToCX,CCZToCX,CRzToCX,CRyToCX,CRxToCX,CR1ToCX},quake-to-cc-prep,func.func(expand-control-veqs,combine-quantum-alloc,canonicalize,combine-measurements),symbol-dce"
+      # Tell the rest-qpu that we are generating OpenQASM 2.0.
+      # As of the time of this writing, qasm2, qir-base and qir-adaptive are supported.
+      codegen-emission: qasm2
+      # Library mode is only for simulators, physical backends must turn this off
+      library-mode: false
+    
+    # Some examples of target arguments are shown below.
+    # You do not need to add any arguments for your backend if you do not need them.
+    target-arguments:
+      - key: api-key
+        required: true
+        type: string
+        platform-arg: api_key
+        help-string: "API key for Provider Name."
+      - key: url
+        required: false
+        type: string
+        platform-arg: url
+        help-string: "Specify Provider Name API server URL."
+      - key: device
+        required: false
+        type: string
+        platform-arg: device
+        help-string: "Specify the Provider Name quantum device to use."
+
+Update Parent CMakeLists.txt
+----------------------------
+
+Add your provider to the parent CMakeLists.txt file:
+
+.. code-block:: cmake
+
+    # runtime/cudaq/platform/default/rest/helpers/CMakeLists.txt
+    add_subdirectory(provider_name)
+    add_subdirectory(ionq)
+    add_subdirectory(iqm)
+    # ... other providers
+
+Testing
+=======
+
+Unit Tests
+----------
+
+Create unit tests for your server helper:
+
+1. Create a directory structure:
+
+.. code-block:: text
+
+    unittests/backends/provider_name/
+    ├── CMakeLists.txt
+    ├── ProviderNameStartServerAndTest.sh.in
+    └── ProviderNameTester.cpp
+
+2. Implement the test files:
+
+.. code-block:: cmake
+
+    # CMakeLists.txt
+    add_executable(ProviderNameTester ProviderNameTester.cpp)
+    target_link_libraries(ProviderNameTester
+      PRIVATE
+      cudaq-common
+      cudaq
+      gtest_main
+    )
+    
+    configure_file(${CMAKE_CURRENT_SOURCE_DIR}/ProviderNameStartServerAndTest.sh.in
+                  ${CMAKE_CURRENT_BINARY_DIR}/ProviderNameStartServerAndTest.sh @ONLY)
+    
+    add_test(NAME ProviderNameTester COMMAND ${CMAKE_CURRENT_BINARY_DIR}/ProviderNameStartServerAndTest.sh)
+    set_tests_properties(ProviderNameTester PROPERTIES TIMEOUT 120)
+
+3. Create a shell script to start the mock server and run tests:
+
+.. code-block:: bash
+
+    #!/bin/bash
+    
+    # Start the mock server
+    python3 -m utils.mock_qpu.provider_name @PORT@ &
+    SERVER_PID=$!
+    
+    # Wait for server to start
+    sleep 2
+    
+    # Run the test
+    @CMAKE_CURRENT_BINARY_DIR@/ProviderNameTester
+    TEST_STATUS=$?
+    
+    # Kill the server
+    kill $SERVER_PID
+    
+    # Return the test status
+    exit $TEST_STATUS
+
+4. Implement the C++ test:
+
+.. code-block:: cpp
+
+    // ProviderNameTester.cpp
+    #include "common/Logger.h"
+    #include "common/RestClient.h"
+    #include "common/ServerHelper.h"
+    #include "cudaq/platform/quantum_platform.h"
+    #include "gtest/gtest.h"
+    
+    TEST(ProviderNameTester, checkSimpleCircuit) {
+      // Initialize the platform
+      auto platform = cudaq::get_platform();
+      platform->setTargetBackend("provider_name");
+      
+      // Set configuration
+      platform->setBackendParameter("url", "http://localhost:PORT");
+      platform->setBackendParameter("api_key", "test_key");
+      
+      // Create a simple circuit
+      auto kernel = cudaq::make_kernel();
+      auto qubits = kernel.qalloc(2);
+      kernel.h(qubits[0]);
+      kernel.cx(qubits[0], qubits[1]);
+      kernel.mz(qubits);
+      
+      // Execute the circuit
+      auto counts = cudaq::sample(kernel);
+      
+      // Check results
+      EXPECT_EQ(counts.size(), 2);
+      EXPECT_TRUE(counts.has_key("00"));
+      EXPECT_TRUE(counts.has_key("11"));
+    }
+
+Mock Server
+----------
+
+Create a mock server for testing:
+
+.. code-block:: text
+
+    utils/mock_qpu/provider_name/
+    └── __init__.py
+
+Implement the mock server:
+
+.. code-block:: python
+
+    # __init__.py
+    from http.server import BaseHTTPRequestHandler, HTTPServer
+    import json
+    import sys
+    import time
+    
+    class ProviderNameMockServer(BaseHTTPRequestHandler):
+        def _set_headers(self, status_code=200):
+            self.send_response(status_code)
+            self.send_header('Content-type', 'application/json')
+            self.end_headers()
+    
+        def do_POST(self):
+            content_length = int(self.headers['Content-Length'])
+            post_data = self.rfile.read(content_length)
+            data = json.loads(post_data.decode('utf-8'))
+            
+            if self.path == '/jobs':
+                # Create a job
+                response = {
+                    'id': 'job-123',
+                    'status': 'QUEUED'
+                }
+                self._set_headers()
+                self.wfile.write(json.dumps(response).encode())
+            else:
+                self._set_headers(404)
+                self.wfile.write(json.dumps({'error': 'Not found'}).encode())
+    
+        def do_GET(self):
+            if self.path.startswith('/jobs/job-123'):
+                # Return job status and results
+                response = {
+                    'id': 'job-123',
+                    'status': 'COMPLETED',
+                    'results': {
+                        'counts': {
+                            '00': 500,
+                            '11': 500
+                        }
+                    }
+                }
+                self._set_headers()
+                self.wfile.write(json.dumps(response).encode())
+            else:
+                self._set_headers(404)
+                self.wfile.write(json.dumps({'error': 'Not found'}).encode())
+    
+    def startServer(port=8000):
+        server_address = ('', port)
+        httpd = HTTPServer(server_address, ProviderNameMockServer)
+        print(f'Starting mock server on port {port}...')
+        httpd.serve_forever()
+    
+    if __name__ == '__main__':
+        port = int(sys.argv[1]) if len(sys.argv) > 1 else 8000
+        startServer(port)
+
+Python Tests
+-----------
+
+Create Python tests for your backend:
+
+.. code-block:: python
+
+    # python/tests/backends/test_provider_name.py
+    import os
+    import sys
+    import time
+    import pytest
+    from multiprocessing import Process
+    
+    import cudaq
+    from cudaq import spin
+    
+    try:
+        from utils.mock_qpu.provider_name import startServer
+    except:
+        print("Mock qpu not available, skipping Provider Name 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():
+        # Set the targeted QPU
+        cudaq.set_target('provider_name',
+                        url=f'http://localhost:{port}',
+                        api_key="test_key")
+        
+        # Launch the Mock Server
+        p = Process(target=startServer, args=(port,))
+        p.start()
+        time.sleep(1)
+        
+        yield "Running the tests."
+        
+        # Kill the server
+        p.terminate()
+    
+    def test_provider_name_sample():
+        # Create the kernel
+        kernel = cudaq.make_kernel()
+        qubits = kernel.qalloc(2)
+        kernel.h(qubits[0])
+        kernel.cx(qubits[0], qubits[1])
+        kernel.mz(qubits)
+        
+        # Run sample
+        counts = cudaq.sample(kernel)
+        assert len(counts) == 2
+        assert '00' in counts
+        assert '11' in counts
+        
+        # Run sample asynchronously
+        future = cudaq.sample_async(kernel)
+        counts = future.get()
+        assert len(counts) == 2
+        assert '00' in counts
+        assert '11' in counts
path: docs/sphinx/using/backends/new_backends.rst
user: login: nvidia-dobri
body: Ditto. 😅 
created_at: 2025-05-07T22:15:33Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,528 @@
+*************************
+Adding New Backends
path: docs/sphinx/using/backends/new_backends.rst
user: login: khalatepradnya
body: Should we explicitly call out remote hardware integration here?
```suggestion
Adding New Hardware Backends
```
?
created_at: 2025-05-07T22:49:36Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,528 @@
+*************************
+Adding New Backends
+*************************
+
+This guide explains how to integrate a new quantum hardware provider with CUDA-Q. The integration process involves creating a server helper that handles communication with the provider's API, defining configuration files, and implementing necessary tests.
+
+Overview
+========
+
+CUDA-Q supports various quantum hardware providers through a REST-based architecture. To add a new backend, you'll need to:
+
+1. Create a server helper class that handles API communication
+2. Define configuration files for the target
+3. Implement tests to verify functionality
+4. Add documentation for users
+
+The following sections detail each step of this process.
+
+Server Helper Implementation
+===========================
+
+The server helper is the core component that handles communication with the quantum hardware provider's API. It extends the ``ServerHelper`` base class and implements methods for job submission, result retrieval, and other provider-specific functionality.
+
+Directory Structure
+------------------
+
+Create the following directory structure for your new backend (replace ``<provider_name>`` with your provider's name):
+
+.. code-block:: text
+
+    runtime/cudaq/platform/default/rest/helpers/<provider_name>/
+    ├── CMakeLists.txt
+    ├── ProviderNameServerHelper.cpp
+    └── <provider_name>.yml
+
+Server Helper Class
+------------------
+
+Here's a template for implementing a server helper class:
+
+.. code-block:: cpp
+
+    // ProviderNameServerHelper.cpp
+    #include "common/Logger.h"
+    #include "common/RestClient.h"
+    #include "common/ServerHelper.h"
+    #include "cudaq/Support/Version.h"
+    #include "cudaq/utils/cudaq_utils.h"
+    #include <bitset>
+    #include <fstream>
+    #include <iostream>
+    #include <map>
+    #include <regex>
+    #include <sstream>
+    #include <thread>
+    #include <unordered_set>
+    
+    using json = nlohmann::json;
+    
+    namespace cudaq {
+    
+    /// @brief The ProviderNameServerHelper class extends the ServerHelper class
+    /// to handle interactions with the Provider Name server for submitting and
+    /// retrieving quantum computation jobs.
+    class ProviderNameServerHelper : public ServerHelper {
+      static constexpr const char *DEFAULT_URL = "https://api.provider-name.com";
+      static constexpr const char *DEFAULT_VERSION = "v1.0";
+    
+    public:
+      const std::string name() const override { return "<provider_name>"; }
+    
+      /// @brief Example implementation of authentication headers.
+      RestHeaders getHeaders() override {
+        RestHeaders headers;
+        headers["Content-Type"] = "application/json";
+        
+        // Add authentication headers if needed
+        if (backendConfig.count("api_key"))
+          headers["Authorization"] = "Bearer " + backendConfig["api_key"];
+        
+        return headers;
+      }
+    
+      /// @brief Example implementation of backend initialization.
+      void initialize(BackendConfig config) override {
+        cudaq::info("Initializing Provider Name Backend");
+        backendConfig = config;
+        
+        if (!backendConfig.count("url"))
+          backendConfig["url"] = DEFAULT_URL;
+        if (!backendConfig.count("version"))
+          backendConfig["version"] = DEFAULT_VERSION;
+
+        // Set shots if provided
+        if (config.find("shots") != config.end())
+          this->setShots(std::stoul(config["shots"]));
+      }
+    
+      /// @brief Example implementation of simple job creation.
+      ServerJobPayload createJob(std::vector<KernelExecution> &circuitCodes) override {
+        ServerMessage job;
+        job["content"] = circuitCodes[0].code;
+        job["shots"] = shots;
+        
+        RestHeaders headers = getHeaders();
+        std::string path = "/jobs";
+        
+        return std::make_tuple(backendConfig["url"] + path, headers, 
+                              std::vector<ServerMessage>{job});
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string extractJobId(ServerMessage &postResponse) override {
+        if (!postResponse.contains("id"))
+          return "";
+        
+        return postResponse.at("id");
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string constructGetJobPath(ServerMessage &postResponse) override {
+        return extractJobId(postResponse);
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string constructGetJobPath(std::string &jobId) override {
+        return backendConfig["url"] + "/jobs/" + jobId;
+      }
+    
+      /// @brief Example implementation of job status checking.
+      bool jobIsDone(ServerMessage &getJobResponse) override {
+        if (!getJobResponse.contains("status"))
+          return false;
+        
+        std::string status = getJobResponse["status"];
+        return status == "COMPLETED" || status == "FAILED";
+      }
+    
+      /// @brief Example implementation of result processing.
+      cudaq::sample_result processResults(ServerMessage &getJobResponse,
+                                         std::string &jobId) override {
+        cudaq::info("Processing results: {}", getJobResponse.dump());
+        
+        // Extract measurement results from the response
+        auto samplesJson = getJobResponse["results"]["counts"];
+        cudaq::CountsDictionary counts;
+        
+        for (auto &item : samplesJson.items()) {
+          std::string bitstring = item.key();
+          std::size_t count = item.value();
+          counts[bitstring] = count;
+        }
+        
+        // Create an ExecutionResult
+        cudaq::ExecutionResult execResult{counts};
+        
+        // Return the sample_result
+        return cudaq::sample_result{execResult};
+      }
+    
+      /// @brief Example implementation of polling configuration.
+      std::chrono::microseconds
+      nextResultPollingInterval(ServerMessage &postResponse) override {
+        return std::chrono::seconds(5);
+      }
+    };
+    
+    } // namespace cudaq
+    
+    // Register the server helper in the CUDA-Q server helper factory
+    CUDAQ_REGISTER_TYPE(cudaq::ServerHelper, cudaq::ProviderNameServerHelper, <provider_name>)
+
+``CMakeLists.txt``
+--------------
+
+Create a ``CMakeLists.txt`` file for your server helper:
+
+.. code-block:: cmake
+
+    # CMakeLists.txt
+    target_sources(cudaq-rest-qpu PRIVATE ProviderNameServerHelper.cpp)
+    add_target_config(<provider_name>)
+    
+    add_library(cudaq-serverhelper-<provider_name> SHARED ProviderNameServerHelper.cpp)
+    target_link_libraries(cudaq-serverhelper-<provider_name>
+      PUBLIC
+      cudaq-common
+      fmt::fmt-header-only
+    )
+    install(TARGETS cudaq-serverhelper-<provider_name> DESTINATION lib)
+
+Target Configuration
+===================
+
+Create a ``YAML`` configuration file for your target:
+
+.. code-block:: yaml
+
+    # <provider_name>.yml
+    name: "<provider_name>"
+    description: "CUDA-Q target for Provider Name."
+    
+    config:
+      # Tell DefaultQuantumPlatform what QPU subtype to use
+      platform-qpu: remote_rest
+      # Add the rest-qpu library to the link list
+      link-libs: ["-lcudaq-rest-qpu"]
+      # Tell NVQ++ to generate glue code to set the target backend name
+      gen-target-backend: true
+      # Add preprocessor defines to compilation
+      preprocessor-defines: ["-D CUDAQ_QUANTUM_DEVICE"]
+      # Define the lowering pipeline
+      # You will likely need assistance from the CUDA-Q team to set this up correctly
+      platform-lowering-config: "classical-optimization-pipeline,globalize-array-values,func.func(state-prep),unitary-synthesis,canonicalize,apply-op-specialization,aggressive-early-inlining,classical-optimization-pipeline,func.func(lower-to-cfg,canonicalize,multicontrol-decomposition),decomposition{enable-patterns=SToR1,TToR1,R1ToU3,U3ToRotations,CHToCX,CCZToCX,CRzToCX,CRyToCX,CRxToCX,CR1ToCX},quake-to-cc-prep,func.func(expand-control-veqs,combine-quantum-alloc,canonicalize,combine-measurements),symbol-dce"
+      # Tell the rest-qpu that we are generating OpenQASM 2.0.
+      # As of the time of this writing, qasm2, qir-base and qir-adaptive are supported.
+      codegen-emission: qasm2
path: docs/sphinx/using/backends/new_backends.rst
user: login: khalatepradnya
body: ```suggestion
      codegen-emission: qir-base
```
I recommend using QIR as the example since the emulation tests truly run qir output with the built-in simulator.
created_at: 2025-05-07T22:53:42Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,528 @@
+*************************
+Adding New Backends
+*************************
+
+This guide explains how to integrate a new quantum hardware provider with CUDA-Q. The integration process involves creating a server helper that handles communication with the provider's API, defining configuration files, and implementing necessary tests.
+
+Overview
+========
+
+CUDA-Q supports various quantum hardware providers through a REST-based architecture. To add a new backend, you'll need to:
+
+1. Create a server helper class that handles API communication
+2. Define configuration files for the target
+3. Implement tests to verify functionality
+4. Add documentation for users
+
+The following sections detail each step of this process.
+
+Server Helper Implementation
+===========================
+
+The server helper is the core component that handles communication with the quantum hardware provider's API. It extends the ``ServerHelper`` base class and implements methods for job submission, result retrieval, and other provider-specific functionality.
+
+Directory Structure
+------------------
+
+Create the following directory structure for your new backend (replace ``<provider_name>`` with your provider's name):
+
+.. code-block:: text
+
+    runtime/cudaq/platform/default/rest/helpers/<provider_name>/
+    ├── CMakeLists.txt
+    ├── ProviderNameServerHelper.cpp
+    └── <provider_name>.yml
+
+Server Helper Class
+------------------
+
+Here's a template for implementing a server helper class:
+
+.. code-block:: cpp
+
+    // ProviderNameServerHelper.cpp
+    #include "common/Logger.h"
+    #include "common/RestClient.h"
+    #include "common/ServerHelper.h"
+    #include "cudaq/Support/Version.h"
+    #include "cudaq/utils/cudaq_utils.h"
+    #include <bitset>
+    #include <fstream>
+    #include <iostream>
+    #include <map>
+    #include <regex>
+    #include <sstream>
+    #include <thread>
+    #include <unordered_set>
+    
+    using json = nlohmann::json;
+    
+    namespace cudaq {
+    
+    /// @brief The ProviderNameServerHelper class extends the ServerHelper class
+    /// to handle interactions with the Provider Name server for submitting and
+    /// retrieving quantum computation jobs.
+    class ProviderNameServerHelper : public ServerHelper {
+      static constexpr const char *DEFAULT_URL = "https://api.provider-name.com";
+      static constexpr const char *DEFAULT_VERSION = "v1.0";
+    
+    public:
+      const std::string name() const override { return "<provider_name>"; }
+    
+      /// @brief Example implementation of authentication headers.
+      RestHeaders getHeaders() override {
+        RestHeaders headers;
+        headers["Content-Type"] = "application/json";
+        
+        // Add authentication headers if needed
+        if (backendConfig.count("api_key"))
+          headers["Authorization"] = "Bearer " + backendConfig["api_key"];
+        
+        return headers;
+      }
+    
+      /// @brief Example implementation of backend initialization.
+      void initialize(BackendConfig config) override {
+        cudaq::info("Initializing Provider Name Backend");
+        backendConfig = config;
+        
+        if (!backendConfig.count("url"))
+          backendConfig["url"] = DEFAULT_URL;
+        if (!backendConfig.count("version"))
+          backendConfig["version"] = DEFAULT_VERSION;
+
+        // Set shots if provided
+        if (config.find("shots") != config.end())
+          this->setShots(std::stoul(config["shots"]));
+      }
+    
+      /// @brief Example implementation of simple job creation.
+      ServerJobPayload createJob(std::vector<KernelExecution> &circuitCodes) override {
+        ServerMessage job;
+        job["content"] = circuitCodes[0].code;
+        job["shots"] = shots;
+        
+        RestHeaders headers = getHeaders();
+        std::string path = "/jobs";
+        
+        return std::make_tuple(backendConfig["url"] + path, headers, 
+                              std::vector<ServerMessage>{job});
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string extractJobId(ServerMessage &postResponse) override {
+        if (!postResponse.contains("id"))
+          return "";
+        
+        return postResponse.at("id");
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string constructGetJobPath(ServerMessage &postResponse) override {
+        return extractJobId(postResponse);
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string constructGetJobPath(std::string &jobId) override {
+        return backendConfig["url"] + "/jobs/" + jobId;
+      }
+    
+      /// @brief Example implementation of job status checking.
+      bool jobIsDone(ServerMessage &getJobResponse) override {
+        if (!getJobResponse.contains("status"))
+          return false;
+        
+        std::string status = getJobResponse["status"];
+        return status == "COMPLETED" || status == "FAILED";
+      }
+    
+      /// @brief Example implementation of result processing.
+      cudaq::sample_result processResults(ServerMessage &getJobResponse,
+                                         std::string &jobId) override {
+        cudaq::info("Processing results: {}", getJobResponse.dump());
+        
+        // Extract measurement results from the response
+        auto samplesJson = getJobResponse["results"]["counts"];
+        cudaq::CountsDictionary counts;
+        
+        for (auto &item : samplesJson.items()) {
+          std::string bitstring = item.key();
+          std::size_t count = item.value();
+          counts[bitstring] = count;
+        }
+        
+        // Create an ExecutionResult
+        cudaq::ExecutionResult execResult{counts};
+        
+        // Return the sample_result
+        return cudaq::sample_result{execResult};
+      }
+    
+      /// @brief Example implementation of polling configuration.
+      std::chrono::microseconds
+      nextResultPollingInterval(ServerMessage &postResponse) override {
+        return std::chrono::seconds(5);
+      }
+    };
+    
+    } // namespace cudaq
+    
+    // Register the server helper in the CUDA-Q server helper factory
+    CUDAQ_REGISTER_TYPE(cudaq::ServerHelper, cudaq::ProviderNameServerHelper, <provider_name>)
+
+``CMakeLists.txt``
+--------------
+
+Create a ``CMakeLists.txt`` file for your server helper:
+
path: docs/sphinx/using/backends/new_backends.rst
user: login: khalatepradnya
body: With newly added targets we want to make them opt-in with default behavior being that they are always built in CUDA-Q. This will help developers build specific targets and skip them if not working on those aspects.
Suggest to add following -
```
To make your target optional, add a CMake flag in the top-level ``CMakeLists.txt``:

.. code-block:: cmake

    # Enable <provider_name> target by default
    if (NOT DEFINED CUDAQ_ENABLE_PROVIDER_NAME_BACKEND)
      set(CUDAQ_ENABLE_PROVIDER_NAME_BACKEND ON CACHE BOOL "Enable building the <Provider Name> target.")
    endif()

Then, in your target's ``CMakeLists.txt``, check for this flag:

.. code-block:: cmake

    if(CUDAQ_ENABLE_PROVIDER_NAME_BACKEND)
      target_sources(cudaq-rest-qpu PRIVATE ProviderNameServerHelper.cpp)
      add_target_config(<provider_name>)
      
      add_library(cudaq-serverhelper-<provider_name> SHARED ProviderNameServerHelper.cpp)
      target_link_libraries(cudaq-serverhelper-<provider_name>
        PUBLIC
        cudaq-common
        fmt::fmt-header-only
      )
      install(TARGETS cudaq-serverhelper-<provider_name> DESTINATION lib)
    endif()
```

Python tests can be skipped if the target is not built -
```
skipIf<provider>NotInstalled = pytest.mark.skipif(
    not (cudaq.has_target("<provider>")),
    reason='Could not find `<provider>` in installation')
```

C++ test can be checked with following addition to `` targettests/lit.site.cfg.py.in`` file
```
config.cudaq_backends_provider = "@CUDAQ_ENABLE_PROVIDER_NAME_BACKEND@"
if cmake_boolvar_to_bool(config.cudaq_backends_provider):
  config.available_features.add('provider')
  config.substitutions.append(('%provider_avail', 'true'))
else:
  config.substitutions.append(('%provider_avail', 'false'))
```
and
`// RUN: if %provider_avail; then nvq++ %cpp_std --target provider %s -o %t.x; fi`
created_at: 2025-05-07T23:11:40Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,528 @@
+*************************
+Adding New Backends
+*************************
+
+This guide explains how to integrate a new quantum hardware provider with CUDA-Q. The integration process involves creating a server helper that handles communication with the provider's API, defining configuration files, and implementing necessary tests.
+
+Overview
+========
+
+CUDA-Q supports various quantum hardware providers through a REST-based architecture. To add a new backend, you'll need to:
+
+1. Create a server helper class that handles API communication
+2. Define configuration files for the target
+3. Implement tests to verify functionality
+4. Add documentation for users
+
+The following sections detail each step of this process.
+
+Server Helper Implementation
+===========================
+
+The server helper is the core component that handles communication with the quantum hardware provider's API. It extends the ``ServerHelper`` base class and implements methods for job submission, result retrieval, and other provider-specific functionality.
+
+Directory Structure
+------------------
+
+Create the following directory structure for your new backend (replace ``<provider_name>`` with your provider's name):
+
+.. code-block:: text
+
+    runtime/cudaq/platform/default/rest/helpers/<provider_name>/
+    ├── CMakeLists.txt
+    ├── ProviderNameServerHelper.cpp
+    └── <provider_name>.yml
+
+Server Helper Class
+------------------
+
+Here's a template for implementing a server helper class:
+
+.. code-block:: cpp
+
+    // ProviderNameServerHelper.cpp
+    #include "common/Logger.h"
+    #include "common/RestClient.h"
+    #include "common/ServerHelper.h"
+    #include "cudaq/Support/Version.h"
+    #include "cudaq/utils/cudaq_utils.h"
+    #include <bitset>
+    #include <fstream>
+    #include <iostream>
+    #include <map>
+    #include <regex>
+    #include <sstream>
+    #include <thread>
+    #include <unordered_set>
+    
+    using json = nlohmann::json;
+    
+    namespace cudaq {
+    
+    /// @brief The ProviderNameServerHelper class extends the ServerHelper class
+    /// to handle interactions with the Provider Name server for submitting and
+    /// retrieving quantum computation jobs.
+    class ProviderNameServerHelper : public ServerHelper {
+      static constexpr const char *DEFAULT_URL = "https://api.provider-name.com";
+      static constexpr const char *DEFAULT_VERSION = "v1.0";
+    
+    public:
+      const std::string name() const override { return "<provider_name>"; }
+    
+      /// @brief Example implementation of authentication headers.
+      RestHeaders getHeaders() override {
+        RestHeaders headers;
+        headers["Content-Type"] = "application/json";
+        
+        // Add authentication headers if needed
+        if (backendConfig.count("api_key"))
+          headers["Authorization"] = "Bearer " + backendConfig["api_key"];
+        
+        return headers;
+      }
+    
+      /// @brief Example implementation of backend initialization.
+      void initialize(BackendConfig config) override {
+        cudaq::info("Initializing Provider Name Backend");
+        backendConfig = config;
+        
+        if (!backendConfig.count("url"))
+          backendConfig["url"] = DEFAULT_URL;
+        if (!backendConfig.count("version"))
+          backendConfig["version"] = DEFAULT_VERSION;
+
+        // Set shots if provided
+        if (config.find("shots") != config.end())
+          this->setShots(std::stoul(config["shots"]));
+      }
+    
+      /// @brief Example implementation of simple job creation.
+      ServerJobPayload createJob(std::vector<KernelExecution> &circuitCodes) override {
+        ServerMessage job;
+        job["content"] = circuitCodes[0].code;
+        job["shots"] = shots;
+        
+        RestHeaders headers = getHeaders();
+        std::string path = "/jobs";
+        
+        return std::make_tuple(backendConfig["url"] + path, headers, 
+                              std::vector<ServerMessage>{job});
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string extractJobId(ServerMessage &postResponse) override {
+        if (!postResponse.contains("id"))
+          return "";
+        
+        return postResponse.at("id");
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string constructGetJobPath(ServerMessage &postResponse) override {
+        return extractJobId(postResponse);
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string constructGetJobPath(std::string &jobId) override {
+        return backendConfig["url"] + "/jobs/" + jobId;
+      }
+    
+      /// @brief Example implementation of job status checking.
+      bool jobIsDone(ServerMessage &getJobResponse) override {
+        if (!getJobResponse.contains("status"))
+          return false;
+        
+        std::string status = getJobResponse["status"];
+        return status == "COMPLETED" || status == "FAILED";
+      }
+    
+      /// @brief Example implementation of result processing.
path: docs/sphinx/using/backends/new_backends.rst
user: login: khalatepradnya
body: ```suggestion
      /// @brief Example implementation of result processing. 
      The raw results from quantum hardware often need post-processing (bit reordering, normalization, etc.) to match CUDA-Q's expectations. Handle it here.
```
created_at: 2025-05-07T23:15:07Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,528 @@
+*************************
+Adding New Backends
+*************************
+
+This guide explains how to integrate a new quantum hardware provider with CUDA-Q. The integration process involves creating a server helper that handles communication with the provider's API, defining configuration files, and implementing necessary tests.
+
+Overview
+========
+
+CUDA-Q supports various quantum hardware providers through a REST-based architecture. To add a new backend, you'll need to:
+
+1. Create a server helper class that handles API communication
+2. Define configuration files for the target
+3. Implement tests to verify functionality
+4. Add documentation for users
+
+The following sections detail each step of this process.
+
+Server Helper Implementation
+===========================
+
+The server helper is the core component that handles communication with the quantum hardware provider's API. It extends the ``ServerHelper`` base class and implements methods for job submission, result retrieval, and other provider-specific functionality.
+
+Directory Structure
+------------------
+
+Create the following directory structure for your new backend (replace ``<provider_name>`` with your provider's name):
+
+.. code-block:: text
+
+    runtime/cudaq/platform/default/rest/helpers/<provider_name>/
+    ├── CMakeLists.txt
+    ├── ProviderNameServerHelper.cpp
+    └── <provider_name>.yml
+
+Server Helper Class
+------------------
+
+Here's a template for implementing a server helper class:
+
+.. code-block:: cpp
+
+    // ProviderNameServerHelper.cpp
+    #include "common/Logger.h"
+    #include "common/RestClient.h"
+    #include "common/ServerHelper.h"
+    #include "cudaq/Support/Version.h"
+    #include "cudaq/utils/cudaq_utils.h"
+    #include <bitset>
+    #include <fstream>
+    #include <iostream>
+    #include <map>
+    #include <regex>
+    #include <sstream>
+    #include <thread>
+    #include <unordered_set>
+    
+    using json = nlohmann::json;
+    
+    namespace cudaq {
+    
+    /// @brief The ProviderNameServerHelper class extends the ServerHelper class
+    /// to handle interactions with the Provider Name server for submitting and
+    /// retrieving quantum computation jobs.
+    class ProviderNameServerHelper : public ServerHelper {
+      static constexpr const char *DEFAULT_URL = "https://api.provider-name.com";
+      static constexpr const char *DEFAULT_VERSION = "v1.0";
+    
+    public:
+      const std::string name() const override { return "<provider_name>"; }
+    
+      /// @brief Example implementation of authentication headers.
+      RestHeaders getHeaders() override {
+        RestHeaders headers;
+        headers["Content-Type"] = "application/json";
+        
+        // Add authentication headers if needed
+        if (backendConfig.count("api_key"))
+          headers["Authorization"] = "Bearer " + backendConfig["api_key"];
+        
+        return headers;
+      }
+    
+      /// @brief Example implementation of backend initialization.
+      void initialize(BackendConfig config) override {
+        cudaq::info("Initializing Provider Name Backend");
+        backendConfig = config;
+        
+        if (!backendConfig.count("url"))
+          backendConfig["url"] = DEFAULT_URL;
+        if (!backendConfig.count("version"))
+          backendConfig["version"] = DEFAULT_VERSION;
+
+        // Set shots if provided
+        if (config.find("shots") != config.end())
+          this->setShots(std::stoul(config["shots"]));
+      }
+    
+      /// @brief Example implementation of simple job creation.
+      ServerJobPayload createJob(std::vector<KernelExecution> &circuitCodes) override {
+        ServerMessage job;
+        job["content"] = circuitCodes[0].code;
+        job["shots"] = shots;
+        
+        RestHeaders headers = getHeaders();
+        std::string path = "/jobs";
+        
+        return std::make_tuple(backendConfig["url"] + path, headers, 
+                              std::vector<ServerMessage>{job});
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string extractJobId(ServerMessage &postResponse) override {
+        if (!postResponse.contains("id"))
+          return "";
+        
+        return postResponse.at("id");
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string constructGetJobPath(ServerMessage &postResponse) override {
+        return extractJobId(postResponse);
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string constructGetJobPath(std::string &jobId) override {
+        return backendConfig["url"] + "/jobs/" + jobId;
+      }
+    
+      /// @brief Example implementation of job status checking.
+      bool jobIsDone(ServerMessage &getJobResponse) override {
+        if (!getJobResponse.contains("status"))
+          return false;
+        
+        std::string status = getJobResponse["status"];
+        return status == "COMPLETED" || status == "FAILED";
+      }
+    
+      /// @brief Example implementation of result processing.
+      cudaq::sample_result processResults(ServerMessage &getJobResponse,
+                                         std::string &jobId) override {
+        cudaq::info("Processing results: {}", getJobResponse.dump());
+        
+        // Extract measurement results from the response
+        auto samplesJson = getJobResponse["results"]["counts"];
+        cudaq::CountsDictionary counts;
+        
+        for (auto &item : samplesJson.items()) {
+          std::string bitstring = item.key();
+          std::size_t count = item.value();
+          counts[bitstring] = count;
+        }
+        
+        // Create an ExecutionResult
+        cudaq::ExecutionResult execResult{counts};
+        
+        // Return the sample_result
+        return cudaq::sample_result{execResult};
+      }
+    
+      /// @brief Example implementation of polling configuration.
+      std::chrono::microseconds
+      nextResultPollingInterval(ServerMessage &postResponse) override {
+        return std::chrono::seconds(5);
+      }
+    };
+    
+    } // namespace cudaq
+    
+    // Register the server helper in the CUDA-Q server helper factory
+    CUDAQ_REGISTER_TYPE(cudaq::ServerHelper, cudaq::ProviderNameServerHelper, <provider_name>)
+
+``CMakeLists.txt``
+--------------
+
+Create a ``CMakeLists.txt`` file for your server helper:
+
+.. code-block:: cmake
+
+    # CMakeLists.txt
+    target_sources(cudaq-rest-qpu PRIVATE ProviderNameServerHelper.cpp)
+    add_target_config(<provider_name>)
+    
+    add_library(cudaq-serverhelper-<provider_name> SHARED ProviderNameServerHelper.cpp)
+    target_link_libraries(cudaq-serverhelper-<provider_name>
+      PUBLIC
+      cudaq-common
+      fmt::fmt-header-only
+    )
+    install(TARGETS cudaq-serverhelper-<provider_name> DESTINATION lib)
+
+Target Configuration
+===================
+
+Create a ``YAML`` configuration file for your target:
+
+.. code-block:: yaml
+
+    # <provider_name>.yml
+    name: "<provider_name>"
+    description: "CUDA-Q target for Provider Name."
+    
+    config:
+      # Tell DefaultQuantumPlatform what QPU subtype to use
+      platform-qpu: remote_rest
+      # Add the rest-qpu library to the link list
+      link-libs: ["-lcudaq-rest-qpu"]
+      # Tell NVQ++ to generate glue code to set the target backend name
+      gen-target-backend: true
+      # Add preprocessor defines to compilation
+      preprocessor-defines: ["-D CUDAQ_QUANTUM_DEVICE"]
+      # Define the lowering pipeline
+      # You will likely need assistance from the CUDA-Q team to set this up correctly
path: docs/sphinx/using/backends/new_backends.rst
user: login: khalatepradnya
body: +1

```suggestion
      # This will cover applying hardware-specific constraints since each provider may have different native gate sets, requiring custom mappings and decompositions. You may need assistance from the CUDA-Q team to set this up correctly.
```
created_at: 2025-05-07T23:19:44Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,528 @@
+*************************
+Adding New Backends
+*************************
+
+This guide explains how to integrate a new quantum hardware provider with CUDA-Q. The integration process involves creating a server helper that handles communication with the provider's API, defining configuration files, and implementing necessary tests.
+
+Overview
+========
+
+CUDA-Q supports various quantum hardware providers through a REST-based architecture. To add a new backend, you'll need to:
+
+1. Create a server helper class that handles API communication
+2. Define configuration files for the target
+3. Implement tests to verify functionality
+4. Add documentation for users
+
+The following sections detail each step of this process.
+
+Server Helper Implementation
+===========================
+
+The server helper is the core component that handles communication with the quantum hardware provider's API. It extends the ``ServerHelper`` base class and implements methods for job submission, result retrieval, and other provider-specific functionality.
+
+Directory Structure
+------------------
+
+Create the following directory structure for your new backend (replace ``<provider_name>`` with your provider's name):
+
+.. code-block:: text
+
+    runtime/cudaq/platform/default/rest/helpers/<provider_name>/
+    ├── CMakeLists.txt
+    ├── ProviderNameServerHelper.cpp
+    └── <provider_name>.yml
+
+Server Helper Class
+------------------
+
+Here's a template for implementing a server helper class:
+
+.. code-block:: cpp
+
+    // ProviderNameServerHelper.cpp
+    #include "common/Logger.h"
+    #include "common/RestClient.h"
+    #include "common/ServerHelper.h"
+    #include "cudaq/Support/Version.h"
+    #include "cudaq/utils/cudaq_utils.h"
+    #include <bitset>
+    #include <fstream>
+    #include <iostream>
+    #include <map>
+    #include <regex>
+    #include <sstream>
+    #include <thread>
+    #include <unordered_set>
+    
+    using json = nlohmann::json;
+    
+    namespace cudaq {
+    
+    /// @brief The ProviderNameServerHelper class extends the ServerHelper class
+    /// to handle interactions with the Provider Name server for submitting and
+    /// retrieving quantum computation jobs.
+    class ProviderNameServerHelper : public ServerHelper {
+      static constexpr const char *DEFAULT_URL = "https://api.provider-name.com";
+      static constexpr const char *DEFAULT_VERSION = "v1.0";
+    
+    public:
+      const std::string name() const override { return "<provider_name>"; }
+    
+      /// @brief Example implementation of authentication headers.
+      RestHeaders getHeaders() override {
+        RestHeaders headers;
+        headers["Content-Type"] = "application/json";
+        
+        // Add authentication headers if needed
+        if (backendConfig.count("api_key"))
+          headers["Authorization"] = "Bearer " + backendConfig["api_key"];
+        
+        return headers;
+      }
+    
+      /// @brief Example implementation of backend initialization.
+      void initialize(BackendConfig config) override {
+        cudaq::info("Initializing Provider Name Backend");
+        backendConfig = config;
+        
+        if (!backendConfig.count("url"))
+          backendConfig["url"] = DEFAULT_URL;
+        if (!backendConfig.count("version"))
+          backendConfig["version"] = DEFAULT_VERSION;
+
+        // Set shots if provided
+        if (config.find("shots") != config.end())
+          this->setShots(std::stoul(config["shots"]));
+      }
+    
+      /// @brief Example implementation of simple job creation.
+      ServerJobPayload createJob(std::vector<KernelExecution> &circuitCodes) override {
+        ServerMessage job;
+        job["content"] = circuitCodes[0].code;
+        job["shots"] = shots;
+        
+        RestHeaders headers = getHeaders();
+        std::string path = "/jobs";
+        
+        return std::make_tuple(backendConfig["url"] + path, headers, 
+                              std::vector<ServerMessage>{job});
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string extractJobId(ServerMessage &postResponse) override {
+        if (!postResponse.contains("id"))
+          return "";
+        
+        return postResponse.at("id");
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string constructGetJobPath(ServerMessage &postResponse) override {
+        return extractJobId(postResponse);
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string constructGetJobPath(std::string &jobId) override {
+        return backendConfig["url"] + "/jobs/" + jobId;
+      }
+    
+      /// @brief Example implementation of job status checking.
+      bool jobIsDone(ServerMessage &getJobResponse) override {
+        if (!getJobResponse.contains("status"))
+          return false;
+        
+        std::string status = getJobResponse["status"];
+        return status == "COMPLETED" || status == "FAILED";
+      }
+    
+      /// @brief Example implementation of result processing.
+      cudaq::sample_result processResults(ServerMessage &getJobResponse,
+                                         std::string &jobId) override {
+        cudaq::info("Processing results: {}", getJobResponse.dump());
+        
+        // Extract measurement results from the response
+        auto samplesJson = getJobResponse["results"]["counts"];
+        cudaq::CountsDictionary counts;
+        
+        for (auto &item : samplesJson.items()) {
+          std::string bitstring = item.key();
+          std::size_t count = item.value();
+          counts[bitstring] = count;
+        }
+        
+        // Create an ExecutionResult
+        cudaq::ExecutionResult execResult{counts};
+        
+        // Return the sample_result
+        return cudaq::sample_result{execResult};
+      }
+    
+      /// @brief Example implementation of polling configuration.
+      std::chrono::microseconds
+      nextResultPollingInterval(ServerMessage &postResponse) override {
+        return std::chrono::seconds(5);
+      }
+    };
+    
+    } // namespace cudaq
+    
+    // Register the server helper in the CUDA-Q server helper factory
+    CUDAQ_REGISTER_TYPE(cudaq::ServerHelper, cudaq::ProviderNameServerHelper, <provider_name>)
+
+``CMakeLists.txt``
+--------------
+
+Create a ``CMakeLists.txt`` file for your server helper:
+
+.. code-block:: cmake
+
+    # CMakeLists.txt
+    target_sources(cudaq-rest-qpu PRIVATE ProviderNameServerHelper.cpp)
+    add_target_config(<provider_name>)
+    
+    add_library(cudaq-serverhelper-<provider_name> SHARED ProviderNameServerHelper.cpp)
+    target_link_libraries(cudaq-serverhelper-<provider_name>
+      PUBLIC
+      cudaq-common
+      fmt::fmt-header-only
+    )
+    install(TARGETS cudaq-serverhelper-<provider_name> DESTINATION lib)
+
+Target Configuration
+===================
+
+Create a ``YAML`` configuration file for your target:
+
+.. code-block:: yaml
+
+    # <provider_name>.yml
+    name: "<provider_name>"
+    description: "CUDA-Q target for Provider Name."
+    
+    config:
+      # Tell DefaultQuantumPlatform what QPU subtype to use
+      platform-qpu: remote_rest
+      # Add the rest-qpu library to the link list
+      link-libs: ["-lcudaq-rest-qpu"]
+      # Tell NVQ++ to generate glue code to set the target backend name
+      gen-target-backend: true
+      # Add preprocessor defines to compilation
+      preprocessor-defines: ["-D CUDAQ_QUANTUM_DEVICE"]
+      # Define the lowering pipeline
+      # You will likely need assistance from the CUDA-Q team to set this up correctly
+      platform-lowering-config: "classical-optimization-pipeline,globalize-array-values,func.func(state-prep),unitary-synthesis,canonicalize,apply-op-specialization,aggressive-early-inlining,classical-optimization-pipeline,func.func(lower-to-cfg,canonicalize,multicontrol-decomposition),decomposition{enable-patterns=SToR1,TToR1,R1ToU3,U3ToRotations,CHToCX,CCZToCX,CRzToCX,CRyToCX,CRxToCX,CR1ToCX},quake-to-cc-prep,func.func(expand-control-veqs,combine-quantum-alloc,canonicalize,combine-measurements),symbol-dce"
+      # Tell the rest-qpu that we are generating OpenQASM 2.0.
+      # As of the time of this writing, qasm2, qir-base and qir-adaptive are supported.
+      codegen-emission: qasm2
+      # Library mode is only for simulators, physical backends must turn this off
+      library-mode: false
+    
+    # Some examples of target arguments are shown below.
+    # You do not need to add any arguments for your backend if you do not need them.
+    target-arguments:
+      - key: api-key
+        required: true
+        type: string
+        platform-arg: api_key
+        help-string: "API key for Provider Name."
+      - key: url
+        required: false
+        type: string
+        platform-arg: url
+        help-string: "Specify Provider Name API server URL."
+      - key: device
+        required: false
+        type: string
+        platform-arg: device
+        help-string: "Specify the Provider Name quantum device to use."
+
+Update Parent ``CMakeLists.txt``
+----------------------------
+
+Add your provider to the parent ``CMakeLists.txt`` file:
+
+.. code-block:: cmake
+
+    # runtime/cudaq/platform/default/rest/helpers/CMakeLists.txt
+    add_subdirectory(<provider_name>)
+    add_subdirectory(ionq)
+    add_subdirectory(iqm)
+    # ... other providers
+
+Testing
+=======
+
+Unit Tests
+----------
+
+Create unit tests for your server helper:
+
+1. Create a directory structure:
+
+.. code-block:: text
+
+    unittests/backends/<provider_name>/
+    ├── CMakeLists.txt
+    ├── ProviderNameStartServerAndTest.sh.in
+    └── ProviderNameTester.cpp
+
+2. Implement the test files:
+
+.. code-block:: cmake
+
+    # CMakeLists.txt
+    add_executable(ProviderNameTester ProviderNameTester.cpp)
+    target_link_libraries(ProviderNameTester
+      PRIVATE
+      cudaq-common
+      cudaq
+      gtest_main
+    )
+    
+    configure_file(${CMAKE_CURRENT_SOURCE_DIR}/ProviderNameStartServerAndTest.sh.in
+                  ${CMAKE_CURRENT_BINARY_DIR}/ProviderNameStartServerAndTest.sh @ONLY)
+    
+    add_test(NAME ProviderNameTester COMMAND ${CMAKE_CURRENT_BINARY_DIR}/ProviderNameStartServerAndTest.sh)
+    set_tests_properties(ProviderNameTester PROPERTIES TIMEOUT 120)
+
+3. Create a shell script to start the mock server and run tests:
+
+.. code-block:: bash
+
+    #!/bin/bash
+    
+    # Start the mock server
+    python3 -m utils.mock_qpu.<provider_name> @PORT@ &
+    SERVER_PID=$!
+    
+    # Wait for server to start
+    sleep 2
+    
+    # Run the test
+    @CMAKE_CURRENT_BINARY_DIR@/ProviderNameTester
+    TEST_STATUS=$?
+    
+    # Kill the server
+    kill $SERVER_PID
+    
+    # Return the test status
+    exit $TEST_STATUS
+
+4. Implement the C++ test:
+
+.. code-block:: cpp
+
+    // ProviderNameTester.cpp
+    #include "common/Logger.h"
+    #include "common/RestClient.h"
+    #include "common/ServerHelper.h"
+    #include "cudaq/platform/quantum_platform.h"
+    #include "gtest/gtest.h"
+    
+    TEST(ProviderNameTester, checkSimpleCircuit) {
+      // Initialize the platform
+      auto platform = cudaq::get_platform();
+      platform->setTargetBackend("<provider_name>");
+      
+      // Set configuration
+      platform->setBackendParameter("url", "http://localhost:PORT");
+      platform->setBackendParameter("api_key", "test_key");
+      
+      // Create a simple circuit
+      auto kernel = cudaq::make_kernel();
+      auto qubits = kernel.qalloc(2);
+      kernel.h(qubits[0]);
+      kernel.cx(qubits[0], qubits[1]);
+      kernel.mz(qubits);
+      
+      // Execute the circuit
+      auto counts = cudaq::sample(kernel);
+      
+      // Check results
+      EXPECT_EQ(counts.size(), 2);
+      EXPECT_TRUE(counts.has_key("00"));
+      EXPECT_TRUE(counts.has_key("11"));
+    }
+
+Mock Server
+----------
+
+Create a mock server for testing:
+
+.. code-block:: text
+
+    utils/mock_qpu/<provider_name>/
+    └── __init__.py
+
+Implement the mock server:
+
+.. code-block:: python
+
+    # __init__.py
+    from http.server import BaseHTTPRequestHandler, HTTPServer
+    import json
+    import sys
+    import time
+    
+    class ProviderNameMockServer(BaseHTTPRequestHandler):
+        def _set_headers(self, status_code=200):
+            self.send_response(status_code)
+            self.send_header('Content-type', 'application/json')
+            self.end_headers()
+    
+        def do_POST(self):
+            content_length = int(self.headers['Content-Length'])
+            post_data = self.rfile.read(content_length)
+            data = json.loads(post_data.decode('utf-8'))
+            
+            if self.path == '/jobs':
+                # Create a job
+                response = {
+                    'id': 'job-123',
+                    'status': 'QUEUED'
+                }
+                self._set_headers()
+                self.wfile.write(json.dumps(response).encode())
+            else:
+                self._set_headers(404)
+                self.wfile.write(json.dumps({'error': 'Not found'}).encode())
+    
+        def do_GET(self):
+            if self.path.startswith('/jobs/job-123'):
+                # Return job status and results
+                response = {
+                    'id': 'job-123',
+                    'status': 'COMPLETED',
+                    'results': {
+                        'counts': {
+                            '00': 500,
+                            '11': 500
+                        }
+                    }
+                }
+                self._set_headers()
+                self.wfile.write(json.dumps(response).encode())
+            else:
+                self._set_headers(404)
+                self.wfile.write(json.dumps({'error': 'Not found'}).encode())
+    
+    def startServer(port=8000):
+        server_address = ('', port)
+        httpd = HTTPServer(server_address, ProviderNameMockServer)
+        print(f'Starting mock server on port {port}...')
+        httpd.serve_forever()
+    
+    if __name__ == '__main__':
+        port = int(sys.argv[1]) if len(sys.argv) > 1 else 8000
+        startServer(port)
+
+Python Tests
+-----------
+
+Create Python tests for your backend:
+
+.. code-block:: python
+
+    # python/tests/backends/test_<provider_name>.py
+    import os
+    import sys
+    import time
+    import pytest
+    from multiprocessing import Process
+    
+    import cudaq
+    from cudaq import spin
+    
+    try:
+        from utils.mock_qpu.<provider_name> import startServer
+    except:
+        print("Mock qpu not available, skipping Provider Name 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():
+        # Set the targeted QPU
+        cudaq.set_target('<provider_name>',
+                        url=f'http://localhost:{port}',
+                        api_key="test_key")
+        
+        # Launch the Mock Server
+        p = Process(target=startServer, args=(port,))
+        p.start()
+        time.sleep(1)
+        
+        yield "Running the tests."
+        
+        # Kill the server
+        p.terminate()
+    
+    def test_<provider_name>_sample():
+        # Create the kernel
+        kernel = cudaq.make_kernel()
+        qubits = kernel.qalloc(2)
+        kernel.h(qubits[0])
+        kernel.cx(qubits[0], qubits[1])
+        kernel.mz(qubits)
+        
+        # Run sample
+        counts = cudaq.sample(kernel)
+        assert len(counts) == 2
+        assert '00' in counts
+        assert '11' in counts
+        
+        # Run sample asynchronously
+        future = cudaq.sample_async(kernel)
+        counts = future.get()
+        assert len(counts) == 2
+        assert '00' in counts
+        assert '11' in counts
+
+Documentation
path: docs/sphinx/using/backends/new_backends.rst
user: login: khalatepradnya
body: Should we add a section to specifically call out integration testing requirements?
```
To ensure proper execution on the hardware, a validation backend must be provided that:

1. Consumes the same format that your target will use
2. Validates that circuits passing this validation will execute successfully on the actual hardware
3. Can be accessed by CUDA-Q's GitHub CI/CD pipelines

Your validation backend doesn't need to be publicly available, but it should:

- Accept the same input format as your actual quantum processor
- Return meaningful error messages for invalid circuits
- Provide an API endpoint that can be called from our integration tests
```

followed by how-to
```
Add your target to ``.github/workflows/integration_tests.yml``:

.. code-block:: yaml

    - name: Submit to <provider_name> test server
      if: (success() || failure()) && (inputs.target == '<provider_name>' || github.event_name == 'schedule')
      run: |
        echo "### Submit to <provider_name> server" >> $GITHUB_STEP_SUMMARY
        # Set up any required dependencies        
        # Set up environment variables for authentication
        export PROVIDER_API_KEY='${{ secrets.PROVIDER_API_KEY }}'
        
        # Run the integration tests
        python_tests="docs/sphinx/targets/python/<provider_name>.py"
        cpp_tests="docs/sphinx/targets/cpp/<provider_name>.cpp"
        
        # Execute tests (see other provider examples for implementation details)
        # ...
```
created_at: 2025-05-07T23:24:42Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,528 @@
+*************************
+Adding New Backends
+*************************
+
+This guide explains how to integrate a new quantum hardware provider with CUDA-Q. The integration process involves creating a server helper that handles communication with the provider's API, defining configuration files, and implementing necessary tests.
+
+Overview
+========
+
+CUDA-Q supports various quantum hardware providers through a REST-based architecture. To add a new backend, you'll need to:
+
+1. Create a server helper class that handles API communication
+2. Define configuration files for the target
+3. Implement tests to verify functionality
+4. Add documentation for users
+
+The following sections detail each step of this process.
+
+Server Helper Implementation
+===========================
+
+The server helper is the core component that handles communication with the quantum hardware provider's API. It extends the ``ServerHelper`` base class and implements methods for job submission, result retrieval, and other provider-specific functionality.
+
+Directory Structure
+------------------
+
+Create the following directory structure for your new backend (replace ``<provider_name>`` with your provider's name):
+
+.. code-block:: text
+
+    runtime/cudaq/platform/default/rest/helpers/<provider_name>/
+    ├── CMakeLists.txt
+    ├── ProviderNameServerHelper.cpp
+    └── <provider_name>.yml
+
+Server Helper Class
+------------------
+
+Here's a template for implementing a server helper class:
+
+.. code-block:: cpp
+
+    // ProviderNameServerHelper.cpp
+    #include "common/Logger.h"
+    #include "common/RestClient.h"
+    #include "common/ServerHelper.h"
+    #include "cudaq/Support/Version.h"
+    #include "cudaq/utils/cudaq_utils.h"
+    #include <bitset>
+    #include <fstream>
+    #include <iostream>
+    #include <map>
+    #include <regex>
+    #include <sstream>
+    #include <thread>
+    #include <unordered_set>
+    
+    using json = nlohmann::json;
+    
+    namespace cudaq {
+    
+    /// @brief The ProviderNameServerHelper class extends the ServerHelper class
+    /// to handle interactions with the Provider Name server for submitting and
+    /// retrieving quantum computation jobs.
+    class ProviderNameServerHelper : public ServerHelper {
+      static constexpr const char *DEFAULT_URL = "https://api.provider-name.com";
+      static constexpr const char *DEFAULT_VERSION = "v1.0";
+    
+    public:
+      const std::string name() const override { return "<provider_name>"; }
+    
+      /// @brief Example implementation of authentication headers.
+      RestHeaders getHeaders() override {
+        RestHeaders headers;
+        headers["Content-Type"] = "application/json";
+        
+        // Add authentication headers if needed
+        if (backendConfig.count("api_key"))
+          headers["Authorization"] = "Bearer " + backendConfig["api_key"];
+        
+        return headers;
+      }
+    
+      /// @brief Example implementation of backend initialization.
+      void initialize(BackendConfig config) override {
+        cudaq::info("Initializing Provider Name Backend");
+        backendConfig = config;
+        
+        if (!backendConfig.count("url"))
+          backendConfig["url"] = DEFAULT_URL;
+        if (!backendConfig.count("version"))
+          backendConfig["version"] = DEFAULT_VERSION;
+
+        // Set shots if provided
+        if (config.find("shots") != config.end())
+          this->setShots(std::stoul(config["shots"]));
+      }
+    
+      /// @brief Example implementation of simple job creation.
+      ServerJobPayload createJob(std::vector<KernelExecution> &circuitCodes) override {
+        ServerMessage job;
+        job["content"] = circuitCodes[0].code;
+        job["shots"] = shots;
+        
+        RestHeaders headers = getHeaders();
+        std::string path = "/jobs";
+        
+        return std::make_tuple(backendConfig["url"] + path, headers, 
+                              std::vector<ServerMessage>{job});
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string extractJobId(ServerMessage &postResponse) override {
+        if (!postResponse.contains("id"))
+          return "";
+        
+        return postResponse.at("id");
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string constructGetJobPath(ServerMessage &postResponse) override {
+        return extractJobId(postResponse);
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string constructGetJobPath(std::string &jobId) override {
+        return backendConfig["url"] + "/jobs/" + jobId;
+      }
+    
+      /// @brief Example implementation of job status checking.
+      bool jobIsDone(ServerMessage &getJobResponse) override {
+        if (!getJobResponse.contains("status"))
+          return false;
+        
+        std::string status = getJobResponse["status"];
+        return status == "COMPLETED" || status == "FAILED";
+      }
+    
+      /// @brief Example implementation of result processing.
+      cudaq::sample_result processResults(ServerMessage &getJobResponse,
+                                         std::string &jobId) override {
+        cudaq::info("Processing results: {}", getJobResponse.dump());
+        
+        // Extract measurement results from the response
+        auto samplesJson = getJobResponse["results"]["counts"];
+        cudaq::CountsDictionary counts;
+        
+        for (auto &item : samplesJson.items()) {
+          std::string bitstring = item.key();
+          std::size_t count = item.value();
+          counts[bitstring] = count;
+        }
+        
+        // Create an ExecutionResult
+        cudaq::ExecutionResult execResult{counts};
+        
+        // Return the sample_result
+        return cudaq::sample_result{execResult};
+      }
+    
+      /// @brief Example implementation of polling configuration.
+      std::chrono::microseconds
+      nextResultPollingInterval(ServerMessage &postResponse) override {
+        return std::chrono::seconds(5);
+      }
+    };
+    
+    } // namespace cudaq
+    
+    // Register the server helper in the CUDA-Q server helper factory
+    CUDAQ_REGISTER_TYPE(cudaq::ServerHelper, cudaq::ProviderNameServerHelper, <provider_name>)
+
+``CMakeLists.txt``
+--------------
+
+Create a ``CMakeLists.txt`` file for your server helper:
+
+.. code-block:: cmake
+
+    # CMakeLists.txt
+    target_sources(cudaq-rest-qpu PRIVATE ProviderNameServerHelper.cpp)
+    add_target_config(<provider_name>)
+    
+    add_library(cudaq-serverhelper-<provider_name> SHARED ProviderNameServerHelper.cpp)
+    target_link_libraries(cudaq-serverhelper-<provider_name>
+      PUBLIC
+      cudaq-common
+      fmt::fmt-header-only
+    )
+    install(TARGETS cudaq-serverhelper-<provider_name> DESTINATION lib)
+
+Target Configuration
+===================
+
+Create a ``YAML`` configuration file for your target:
+
+.. code-block:: yaml
+
+    # <provider_name>.yml
+    name: "<provider_name>"
+    description: "CUDA-Q target for Provider Name."
+    
+    config:
+      # Tell DefaultQuantumPlatform what QPU subtype to use
+      platform-qpu: remote_rest
+      # Add the rest-qpu library to the link list
+      link-libs: ["-lcudaq-rest-qpu"]
+      # Tell NVQ++ to generate glue code to set the target backend name
+      gen-target-backend: true
+      # Add preprocessor defines to compilation
+      preprocessor-defines: ["-D CUDAQ_QUANTUM_DEVICE"]
+      # Define the lowering pipeline
+      # You will likely need assistance from the CUDA-Q team to set this up correctly
+      platform-lowering-config: "classical-optimization-pipeline,globalize-array-values,func.func(state-prep),unitary-synthesis,canonicalize,apply-op-specialization,aggressive-early-inlining,classical-optimization-pipeline,func.func(lower-to-cfg,canonicalize,multicontrol-decomposition),decomposition{enable-patterns=SToR1,TToR1,R1ToU3,U3ToRotations,CHToCX,CCZToCX,CRzToCX,CRyToCX,CRxToCX,CR1ToCX},quake-to-cc-prep,func.func(expand-control-veqs,combine-quantum-alloc,canonicalize,combine-measurements),symbol-dce"
+      # Tell the rest-qpu that we are generating OpenQASM 2.0.
+      # As of the time of this writing, qasm2, qir-base and qir-adaptive are supported.
+      codegen-emission: qasm2
+      # Library mode is only for simulators, physical backends must turn this off
+      library-mode: false
+    
+    # Some examples of target arguments are shown below.
+    # You do not need to add any arguments for your backend if you do not need them.
+    target-arguments:
+      - key: api-key
+        required: true
+        type: string
+        platform-arg: api_key
+        help-string: "API key for Provider Name."
+      - key: url
+        required: false
+        type: string
+        platform-arg: url
+        help-string: "Specify Provider Name API server URL."
+      - key: device
+        required: false
+        type: string
+        platform-arg: device
+        help-string: "Specify the Provider Name quantum device to use."
+
+Update Parent ``CMakeLists.txt``
+----------------------------
+
+Add your provider to the parent ``CMakeLists.txt`` file:
+
+.. code-block:: cmake
+
+    # runtime/cudaq/platform/default/rest/helpers/CMakeLists.txt
+    add_subdirectory(<provider_name>)
+    add_subdirectory(ionq)
+    add_subdirectory(iqm)
+    # ... other providers
+
+Testing
+=======
+
+Unit Tests
+----------
+
+Create unit tests for your server helper:
+
+1. Create a directory structure:
+
+.. code-block:: text
+
+    unittests/backends/<provider_name>/
+    ├── CMakeLists.txt
+    ├── ProviderNameStartServerAndTest.sh.in
+    └── ProviderNameTester.cpp
+
+2. Implement the test files:
+
+.. code-block:: cmake
+
+    # CMakeLists.txt
+    add_executable(ProviderNameTester ProviderNameTester.cpp)
+    target_link_libraries(ProviderNameTester
+      PRIVATE
+      cudaq-common
+      cudaq
+      gtest_main
+    )
+    
+    configure_file(${CMAKE_CURRENT_SOURCE_DIR}/ProviderNameStartServerAndTest.sh.in
+                  ${CMAKE_CURRENT_BINARY_DIR}/ProviderNameStartServerAndTest.sh @ONLY)
+    
+    add_test(NAME ProviderNameTester COMMAND ${CMAKE_CURRENT_BINARY_DIR}/ProviderNameStartServerAndTest.sh)
+    set_tests_properties(ProviderNameTester PROPERTIES TIMEOUT 120)
+
+3. Create a shell script to start the mock server and run tests:
+
+.. code-block:: bash
+
+    #!/bin/bash
+    
+    # Start the mock server
+    python3 -m utils.mock_qpu.<provider_name> @PORT@ &
+    SERVER_PID=$!
+    
+    # Wait for server to start
+    sleep 2
+    
+    # Run the test
+    @CMAKE_CURRENT_BINARY_DIR@/ProviderNameTester
+    TEST_STATUS=$?
+    
+    # Kill the server
+    kill $SERVER_PID
+    
+    # Return the test status
+    exit $TEST_STATUS
+
+4. Implement the C++ test:
+
+.. code-block:: cpp
+
+    // ProviderNameTester.cpp
+    #include "common/Logger.h"
+    #include "common/RestClient.h"
+    #include "common/ServerHelper.h"
+    #include "cudaq/platform/quantum_platform.h"
+    #include "gtest/gtest.h"
+    
+    TEST(ProviderNameTester, checkSimpleCircuit) {
+      // Initialize the platform
+      auto platform = cudaq::get_platform();
+      platform->setTargetBackend("<provider_name>");
+      
+      // Set configuration
+      platform->setBackendParameter("url", "http://localhost:PORT");
+      platform->setBackendParameter("api_key", "test_key");
+      
+      // Create a simple circuit
+      auto kernel = cudaq::make_kernel();
+      auto qubits = kernel.qalloc(2);
+      kernel.h(qubits[0]);
+      kernel.cx(qubits[0], qubits[1]);
+      kernel.mz(qubits);
+      
+      // Execute the circuit
+      auto counts = cudaq::sample(kernel);
+      
+      // Check results
+      EXPECT_EQ(counts.size(), 2);
+      EXPECT_TRUE(counts.has_key("00"));
+      EXPECT_TRUE(counts.has_key("11"));
+    }
+
+Mock Server
+----------
+
+Create a mock server for testing:
+
+.. code-block:: text
+
+    utils/mock_qpu/<provider_name>/
+    └── __init__.py
+
+Implement the mock server:
+
+.. code-block:: python
+
+    # __init__.py
+    from http.server import BaseHTTPRequestHandler, HTTPServer
+    import json
+    import sys
+    import time
+    
+    class ProviderNameMockServer(BaseHTTPRequestHandler):
+        def _set_headers(self, status_code=200):
+            self.send_response(status_code)
+            self.send_header('Content-type', 'application/json')
+            self.end_headers()
+    
+        def do_POST(self):
+            content_length = int(self.headers['Content-Length'])
+            post_data = self.rfile.read(content_length)
+            data = json.loads(post_data.decode('utf-8'))
+            
+            if self.path == '/jobs':
+                # Create a job
+                response = {
+                    'id': 'job-123',
+                    'status': 'QUEUED'
+                }
+                self._set_headers()
+                self.wfile.write(json.dumps(response).encode())
+            else:
+                self._set_headers(404)
+                self.wfile.write(json.dumps({'error': 'Not found'}).encode())
+    
+        def do_GET(self):
+            if self.path.startswith('/jobs/job-123'):
+                # Return job status and results
+                response = {
+                    'id': 'job-123',
+                    'status': 'COMPLETED',
+                    'results': {
+                        'counts': {
+                            '00': 500,
+                            '11': 500
+                        }
+                    }
+                }
+                self._set_headers()
+                self.wfile.write(json.dumps(response).encode())
+            else:
+                self._set_headers(404)
+                self.wfile.write(json.dumps({'error': 'Not found'}).encode())
+    
+    def startServer(port=8000):
+        server_address = ('', port)
+        httpd = HTTPServer(server_address, ProviderNameMockServer)
+        print(f'Starting mock server on port {port}...')
+        httpd.serve_forever()
+    
+    if __name__ == '__main__':
+        port = int(sys.argv[1]) if len(sys.argv) > 1 else 8000
+        startServer(port)
+
+Python Tests
+-----------
+
+Create Python tests for your backend:
+
+.. code-block:: python
+
+    # python/tests/backends/test_<provider_name>.py
+    import os
+    import sys
+    import time
+    import pytest
+    from multiprocessing import Process
+    
+    import cudaq
+    from cudaq import spin
+    
+    try:
+        from utils.mock_qpu.<provider_name> import startServer
+    except:
+        print("Mock qpu not available, skipping Provider Name 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():
+        # Set the targeted QPU
+        cudaq.set_target('<provider_name>',
+                        url=f'http://localhost:{port}',
+                        api_key="test_key")
+        
+        # Launch the Mock Server
+        p = Process(target=startServer, args=(port,))
+        p.start()
+        time.sleep(1)
+        
+        yield "Running the tests."
+        
+        # Kill the server
+        p.terminate()
+    
+    def test_<provider_name>_sample():
+        # Create the kernel
+        kernel = cudaq.make_kernel()
+        qubits = kernel.qalloc(2)
+        kernel.h(qubits[0])
+        kernel.cx(qubits[0], qubits[1])
+        kernel.mz(qubits)
+        
+        # Run sample
+        counts = cudaq.sample(kernel)
+        assert len(counts) == 2
+        assert '00' in counts
+        assert '11' in counts
+        
+        # Run sample asynchronously
+        future = cudaq.sample_async(kernel)
+        counts = future.get()
+        assert len(counts) == 2
+        assert '00' in counts
+        assert '11' in counts
+
+Documentation
+============
+
+Add documentation for your backend in the appropriate sections of the CUDA-Q documentation. This should include:
+
path: docs/sphinx/using/backends/new_backends.rst
user: login: khalatepradnya
body: ```suggestion
0. How to access your server (authentication set up, documentation etc.)
```
?
created_at: 2025-05-07T23:28:39Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,528 @@
+*************************
+Adding New Backends
+*************************
+
+This guide explains how to integrate a new quantum hardware provider with CUDA-Q. The integration process involves creating a server helper that handles communication with the provider's API, defining configuration files, and implementing necessary tests.
+
+Overview
+========
+
+CUDA-Q supports various quantum hardware providers through a REST-based architecture. To add a new backend, you'll need to:
path: docs/sphinx/using/backends/new_backends.rst
user: login: efratshabtai
body: ```suggestion
CUDA-Q supports various quantum hardware [backends](https://nvidia.github.io/cuda-quantum/latest/using/backends/hardware.html) through a REST-based architecture. To add a new backend, you'll need to:
```
created_at: 2025-05-12T15:59:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,528 @@
+*************************
+Adding New Backends
+*************************
+
+This guide explains how to integrate a new quantum hardware provider with CUDA-Q. The integration process involves creating a server helper that handles communication with the provider's API, defining configuration files, and implementing necessary tests.
+
+Overview
+========
+
+CUDA-Q supports various quantum hardware providers through a REST-based architecture. To add a new backend, you'll need to:
+
+1. Create a server helper class that handles API communication
+2. Define configuration files for the target
+3. Implement tests to verify functionality
+4. Add documentation for users
+
+The following sections detail each step of this process.
+
+Server Helper Implementation
+===========================
+
+The server helper is the core component that handles communication with the quantum hardware provider's API. It extends the ``ServerHelper`` base class and implements methods for job submission, result retrieval, and other provider-specific functionality.
path: docs/sphinx/using/backends/new_backends.rst
user: login: efratshabtai
body: add link to the code definition of Server Helper
created_at: 2025-05-12T16:09:07Z
author_association: CONTRIBUTOR
path: docs/sphinx/using/backends/new_backends.rst
user: login: efratshabtai
body: I think this page should be under the 'Extending' section of the docs. We can add a line from the [Backends](https://nvidia.github.io/cuda-quantum/latest/using/backends/backends.html) page pointing to it (to add a new hardware backend please see...)
created_at: 2025-05-12T16:12:26Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,528 @@
+*************************
+Adding New Backends
+*************************
+
+This guide explains how to integrate a new quantum hardware provider with CUDA-Q. The integration process involves creating a server helper that handles communication with the provider's API, defining configuration files, and implementing necessary tests.
+
+Overview
+========
+
+CUDA-Q supports various quantum hardware providers through a REST-based architecture. To add a new backend, you'll need to:
+
+1. Create a server helper class that handles API communication
+2. Define configuration files for the target
+3. Implement tests to verify functionality
+4. Add documentation for users
+
+The following sections detail each step of this process.
+
+Server Helper Implementation
+===========================
+
+The server helper is the core component that handles communication with the quantum hardware provider's API. It extends the ``ServerHelper`` base class and implements methods for job submission, result retrieval, and other provider-specific functionality.
+
+Directory Structure
+------------------
+
+Create the following directory structure for your new backend (replace ``<provider_name>`` with your provider's name):
+
+.. code-block:: text
+
+    runtime/cudaq/platform/default/rest/helpers/<provider_name>/
+    ├── CMakeLists.txt
+    ├── ProviderNameServerHelper.cpp
+    └── <provider_name>.yml
+
+Server Helper Class
+------------------
+
+Here's a template for implementing a server helper class:
+
+.. code-block:: cpp
+
+    // ProviderNameServerHelper.cpp
+    #include "common/Logger.h"
+    #include "common/RestClient.h"
+    #include "common/ServerHelper.h"
+    #include "cudaq/Support/Version.h"
+    #include "cudaq/utils/cudaq_utils.h"
+    #include <bitset>
+    #include <fstream>
+    #include <iostream>
+    #include <map>
+    #include <regex>
+    #include <sstream>
+    #include <thread>
+    #include <unordered_set>
+    
+    using json = nlohmann::json;
+    
+    namespace cudaq {
+    
+    /// @brief The ProviderNameServerHelper class extends the ServerHelper class
+    /// to handle interactions with the Provider Name server for submitting and
+    /// retrieving quantum computation jobs.
+    class ProviderNameServerHelper : public ServerHelper {
+      static constexpr const char *DEFAULT_URL = "https://api.provider-name.com";
+      static constexpr const char *DEFAULT_VERSION = "v1.0";
+    
+    public:
+      const std::string name() const override { return "<provider_name>"; }
+    
+      /// @brief Example implementation of authentication headers.
+      RestHeaders getHeaders() override {
+        RestHeaders headers;
+        headers["Content-Type"] = "application/json";
+        
+        // Add authentication headers if needed
+        if (backendConfig.count("api_key"))
+          headers["Authorization"] = "Bearer " + backendConfig["api_key"];
+        
+        return headers;
+      }
+    
+      /// @brief Example implementation of backend initialization.
+      void initialize(BackendConfig config) override {
+        cudaq::info("Initializing Provider Name Backend");
+        backendConfig = config;
+        
+        if (!backendConfig.count("url"))
+          backendConfig["url"] = DEFAULT_URL;
+        if (!backendConfig.count("version"))
+          backendConfig["version"] = DEFAULT_VERSION;
+
+        // Set shots if provided
+        if (config.find("shots") != config.end())
+          this->setShots(std::stoul(config["shots"]));
+      }
+    
+      /// @brief Example implementation of simple job creation.
+      ServerJobPayload createJob(std::vector<KernelExecution> &circuitCodes) override {
+        ServerMessage job;
+        job["content"] = circuitCodes[0].code;
+        job["shots"] = shots;
+        
+        RestHeaders headers = getHeaders();
+        std::string path = "/jobs";
+        
+        return std::make_tuple(backendConfig["url"] + path, headers, 
+                              std::vector<ServerMessage>{job});
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string extractJobId(ServerMessage &postResponse) override {
+        if (!postResponse.contains("id"))
+          return "";
+        
+        return postResponse.at("id");
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string constructGetJobPath(ServerMessage &postResponse) override {
+        return extractJobId(postResponse);
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string constructGetJobPath(std::string &jobId) override {
+        return backendConfig["url"] + "/jobs/" + jobId;
+      }
+    
+      /// @brief Example implementation of job status checking.
+      bool jobIsDone(ServerMessage &getJobResponse) override {
+        if (!getJobResponse.contains("status"))
+          return false;
+        
+        std::string status = getJobResponse["status"];
+        return status == "COMPLETED" || status == "FAILED";
+      }
+    
+      /// @brief Example implementation of result processing.
+      cudaq::sample_result processResults(ServerMessage &getJobResponse,
+                                         std::string &jobId) override {
+        cudaq::info("Processing results: {}", getJobResponse.dump());
+        
+        // Extract measurement results from the response
+        auto samplesJson = getJobResponse["results"]["counts"];
+        cudaq::CountsDictionary counts;
+        
+        for (auto &item : samplesJson.items()) {
+          std::string bitstring = item.key();
+          std::size_t count = item.value();
+          counts[bitstring] = count;
+        }
+        
+        // Create an ExecutionResult
+        cudaq::ExecutionResult execResult{counts};
+        
+        // Return the sample_result
+        return cudaq::sample_result{execResult};
+      }
+    
+      /// @brief Example implementation of polling configuration.
+      std::chrono::microseconds
+      nextResultPollingInterval(ServerMessage &postResponse) override {
+        return std::chrono::seconds(5);
+      }
+    };
+    
+    } // namespace cudaq
+    
+    // Register the server helper in the CUDA-Q server helper factory
+    CUDAQ_REGISTER_TYPE(cudaq::ServerHelper, cudaq::ProviderNameServerHelper, <provider_name>)
+
+``CMakeLists.txt``
+--------------
+
+Create a ``CMakeLists.txt`` file for your server helper:
+
+.. code-block:: cmake
+
+    # CMakeLists.txt
+    target_sources(cudaq-rest-qpu PRIVATE ProviderNameServerHelper.cpp)
+    add_target_config(<provider_name>)
+    
+    add_library(cudaq-serverhelper-<provider_name> SHARED ProviderNameServerHelper.cpp)
+    target_link_libraries(cudaq-serverhelper-<provider_name>
+      PUBLIC
+      cudaq-common
+      fmt::fmt-header-only
+    )
+    install(TARGETS cudaq-serverhelper-<provider_name> DESTINATION lib)
+
+Target Configuration
+===================
+
+Create a ``YAML`` configuration file for your target:
+
+.. code-block:: yaml
+
+    # <provider_name>.yml
+    name: "<provider_name>"
+    description: "CUDA-Q target for Provider Name."
+    
+    config:
+      # Tell DefaultQuantumPlatform what QPU subtype to use
+      platform-qpu: remote_rest
+      # Add the rest-qpu library to the link list
+      link-libs: ["-lcudaq-rest-qpu"]
+      # Tell NVQ++ to generate glue code to set the target backend name
+      gen-target-backend: true
+      # Add preprocessor defines to compilation
+      preprocessor-defines: ["-D CUDAQ_QUANTUM_DEVICE"]
+      # Define the lowering pipeline
+      # You will likely need assistance from the CUDA-Q team to set this up correctly
+      platform-lowering-config: "classical-optimization-pipeline,globalize-array-values,func.func(state-prep),unitary-synthesis,canonicalize,apply-op-specialization,aggressive-early-inlining,classical-optimization-pipeline,func.func(lower-to-cfg,canonicalize,multicontrol-decomposition),decomposition{enable-patterns=SToR1,TToR1,R1ToU3,U3ToRotations,CHToCX,CCZToCX,CRzToCX,CRyToCX,CRxToCX,CR1ToCX},quake-to-cc-prep,func.func(expand-control-veqs,combine-quantum-alloc,canonicalize,combine-measurements),symbol-dce"
+      # Tell the rest-qpu that we are generating OpenQASM 2.0.
+      # As of the time of this writing, qasm2, qir-base and qir-adaptive are supported.
+      codegen-emission: qasm2
+      # Library mode is only for simulators, physical backends must turn this off
+      library-mode: false
+    
+    # Some examples of target arguments are shown below.
+    # You do not need to add any arguments for your backend if you do not need them.
+    target-arguments:
+      - key: api-key
+        required: true
+        type: string
+        platform-arg: api_key
+        help-string: "API key for Provider Name."
+      - key: url
+        required: false
+        type: string
+        platform-arg: url
+        help-string: "Specify Provider Name API server URL."
+      - key: device
+        required: false
+        type: string
+        platform-arg: device
+        help-string: "Specify the Provider Name quantum device to use."
+
+Update Parent ``CMakeLists.txt``
+----------------------------
+
+Add your provider to the parent ``CMakeLists.txt`` file:
+
+.. code-block:: cmake
+
+    # runtime/cudaq/platform/default/rest/helpers/CMakeLists.txt
+    add_subdirectory(<provider_name>)
+    add_subdirectory(ionq)
+    add_subdirectory(iqm)
+    # ... other providers
+
+Testing
+=======
+
+Unit Tests
+----------
+
+Create unit tests for your server helper:
+
+1. Create a directory structure:
+
+.. code-block:: text
+
+    unittests/backends/<provider_name>/
+    ├── CMakeLists.txt
+    ├── ProviderNameStartServerAndTest.sh.in
+    └── ProviderNameTester.cpp
+
+2. Implement the test files:
+
+.. code-block:: cmake
+
+    # CMakeLists.txt
+    add_executable(ProviderNameTester ProviderNameTester.cpp)
+    target_link_libraries(ProviderNameTester
+      PRIVATE
+      cudaq-common
+      cudaq
+      gtest_main
+    )
+    
+    configure_file(${CMAKE_CURRENT_SOURCE_DIR}/ProviderNameStartServerAndTest.sh.in
+                  ${CMAKE_CURRENT_BINARY_DIR}/ProviderNameStartServerAndTest.sh @ONLY)
+    
+    add_test(NAME ProviderNameTester COMMAND ${CMAKE_CURRENT_BINARY_DIR}/ProviderNameStartServerAndTest.sh)
+    set_tests_properties(ProviderNameTester PROPERTIES TIMEOUT 120)
+
+3. Create a shell script to start the mock server and run tests:
+
+.. code-block:: bash
+
+    #!/bin/bash
+    
+    # Start the mock server
+    python3 -m utils.mock_qpu.<provider_name> @PORT@ &
+    SERVER_PID=$!
+    
+    # Wait for server to start
+    sleep 2
+    
+    # Run the test
+    @CMAKE_CURRENT_BINARY_DIR@/ProviderNameTester
+    TEST_STATUS=$?
+    
+    # Kill the server
+    kill $SERVER_PID
+    
+    # Return the test status
+    exit $TEST_STATUS
+
+4. Implement the C++ test:
+
+.. code-block:: cpp
+
+    // ProviderNameTester.cpp
+    #include "common/Logger.h"
+    #include "common/RestClient.h"
+    #include "common/ServerHelper.h"
+    #include "cudaq/platform/quantum_platform.h"
+    #include "gtest/gtest.h"
+    
+    TEST(ProviderNameTester, checkSimpleCircuit) {
+      // Initialize the platform
+      auto platform = cudaq::get_platform();
+      platform->setTargetBackend("<provider_name>");
+      
+      // Set configuration
+      platform->setBackendParameter("url", "http://localhost:PORT");
+      platform->setBackendParameter("api_key", "test_key");
+      
+      // Create a simple circuit
+      auto kernel = cudaq::make_kernel();
+      auto qubits = kernel.qalloc(2);
+      kernel.h(qubits[0]);
+      kernel.cx(qubits[0], qubits[1]);
+      kernel.mz(qubits);
+      
+      // Execute the circuit
+      auto counts = cudaq::sample(kernel);
+      
+      // Check results
+      EXPECT_EQ(counts.size(), 2);
+      EXPECT_TRUE(counts.has_key("00"));
+      EXPECT_TRUE(counts.has_key("11"));
+    }
+
+Mock Server
path: docs/sphinx/using/backends/new_backends.rst
user: login: efratshabtai
body: You might want to explain what the mock server is and how it is being used. 
created_at: 2025-05-12T16:17:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,528 @@
+*************************
+Adding New Backends
+*************************
+
+This guide explains how to integrate a new quantum hardware provider with CUDA-Q. The integration process involves creating a server helper that handles communication with the provider's API, defining configuration files, and implementing necessary tests.
+
+Overview
+========
+
+CUDA-Q supports various quantum hardware providers through a REST-based architecture. To add a new backend, you'll need to:
+
+1. Create a server helper class that handles API communication
+2. Define configuration files for the target
+3. Implement tests to verify functionality
+4. Add documentation for users
+
+The following sections detail each step of this process.
+
+Server Helper Implementation
+===========================
+
+The server helper is the core component that handles communication with the quantum hardware provider's API. It extends the ``ServerHelper`` base class and implements methods for job submission, result retrieval, and other provider-specific functionality.
+
+Directory Structure
+------------------
+
+Create the following directory structure for your new backend (replace ``<provider_name>`` with your provider's name):
+
+.. code-block:: text
+
+    runtime/cudaq/platform/default/rest/helpers/<provider_name>/
+    ├── CMakeLists.txt
+    ├── ProviderNameServerHelper.cpp
+    └── <provider_name>.yml
+
+Server Helper Class
+------------------
+
+Here's a template for implementing a server helper class:
+
+.. code-block:: cpp
+
+    // ProviderNameServerHelper.cpp
+    #include "common/Logger.h"
+    #include "common/RestClient.h"
+    #include "common/ServerHelper.h"
+    #include "cudaq/Support/Version.h"
+    #include "cudaq/utils/cudaq_utils.h"
+    #include <bitset>
+    #include <fstream>
+    #include <iostream>
+    #include <map>
+    #include <regex>
+    #include <sstream>
+    #include <thread>
+    #include <unordered_set>
+    
+    using json = nlohmann::json;
+    
+    namespace cudaq {
+    
+    /// @brief The ProviderNameServerHelper class extends the ServerHelper class
+    /// to handle interactions with the Provider Name server for submitting and
+    /// retrieving quantum computation jobs.
+    class ProviderNameServerHelper : public ServerHelper {
+      static constexpr const char *DEFAULT_URL = "https://api.provider-name.com";
+      static constexpr const char *DEFAULT_VERSION = "v1.0";
+    
+    public:
+      const std::string name() const override { return "<provider_name>"; }
+    
+      /// @brief Example implementation of authentication headers.
+      RestHeaders getHeaders() override {
+        RestHeaders headers;
+        headers["Content-Type"] = "application/json";
+        
+        // Add authentication headers if needed
+        if (backendConfig.count("api_key"))
+          headers["Authorization"] = "Bearer " + backendConfig["api_key"];
+        
+        return headers;
+      }
+    
+      /// @brief Example implementation of backend initialization.
+      void initialize(BackendConfig config) override {
+        cudaq::info("Initializing Provider Name Backend");
+        backendConfig = config;
+        
+        if (!backendConfig.count("url"))
+          backendConfig["url"] = DEFAULT_URL;
+        if (!backendConfig.count("version"))
+          backendConfig["version"] = DEFAULT_VERSION;
+
+        // Set shots if provided
+        if (config.find("shots") != config.end())
+          this->setShots(std::stoul(config["shots"]));
+      }
+    
+      /// @brief Example implementation of simple job creation.
+      ServerJobPayload createJob(std::vector<KernelExecution> &circuitCodes) override {
+        ServerMessage job;
+        job["content"] = circuitCodes[0].code;
+        job["shots"] = shots;
+        
+        RestHeaders headers = getHeaders();
+        std::string path = "/jobs";
+        
+        return std::make_tuple(backendConfig["url"] + path, headers, 
+                              std::vector<ServerMessage>{job});
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string extractJobId(ServerMessage &postResponse) override {
+        if (!postResponse.contains("id"))
+          return "";
+        
+        return postResponse.at("id");
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string constructGetJobPath(ServerMessage &postResponse) override {
+        return extractJobId(postResponse);
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string constructGetJobPath(std::string &jobId) override {
+        return backendConfig["url"] + "/jobs/" + jobId;
+      }
+    
+      /// @brief Example implementation of job status checking.
+      bool jobIsDone(ServerMessage &getJobResponse) override {
+        if (!getJobResponse.contains("status"))
+          return false;
+        
+        std::string status = getJobResponse["status"];
+        return status == "COMPLETED" || status == "FAILED";
+      }
+    
+      /// @brief Example implementation of result processing.
+      cudaq::sample_result processResults(ServerMessage &getJobResponse,
+                                         std::string &jobId) override {
+        cudaq::info("Processing results: {}", getJobResponse.dump());
+        
+        // Extract measurement results from the response
+        auto samplesJson = getJobResponse["results"]["counts"];
+        cudaq::CountsDictionary counts;
+        
+        for (auto &item : samplesJson.items()) {
+          std::string bitstring = item.key();
+          std::size_t count = item.value();
+          counts[bitstring] = count;
+        }
+        
+        // Create an ExecutionResult
+        cudaq::ExecutionResult execResult{counts};
+        
+        // Return the sample_result
+        return cudaq::sample_result{execResult};
+      }
+    
+      /// @brief Example implementation of polling configuration.
+      std::chrono::microseconds
+      nextResultPollingInterval(ServerMessage &postResponse) override {
+        return std::chrono::seconds(5);
+      }
+    };
+    
+    } // namespace cudaq
+    
+    // Register the server helper in the CUDA-Q server helper factory
+    CUDAQ_REGISTER_TYPE(cudaq::ServerHelper, cudaq::ProviderNameServerHelper, <provider_name>)
+
+``CMakeLists.txt``
+--------------
+
+Create a ``CMakeLists.txt`` file for your server helper:
+
+.. code-block:: cmake
+
+    # CMakeLists.txt
+    target_sources(cudaq-rest-qpu PRIVATE ProviderNameServerHelper.cpp)
+    add_target_config(<provider_name>)
+    
+    add_library(cudaq-serverhelper-<provider_name> SHARED ProviderNameServerHelper.cpp)
+    target_link_libraries(cudaq-serverhelper-<provider_name>
+      PUBLIC
+      cudaq-common
+      fmt::fmt-header-only
+    )
+    install(TARGETS cudaq-serverhelper-<provider_name> DESTINATION lib)
+
+Target Configuration
+===================
+
+Create a ``YAML`` configuration file for your target:
+
+.. code-block:: yaml
+
+    # <provider_name>.yml
+    name: "<provider_name>"
+    description: "CUDA-Q target for Provider Name."
+    
+    config:
+      # Tell DefaultQuantumPlatform what QPU subtype to use
+      platform-qpu: remote_rest
+      # Add the rest-qpu library to the link list
+      link-libs: ["-lcudaq-rest-qpu"]
+      # Tell NVQ++ to generate glue code to set the target backend name
+      gen-target-backend: true
+      # Add preprocessor defines to compilation
+      preprocessor-defines: ["-D CUDAQ_QUANTUM_DEVICE"]
+      # Define the lowering pipeline
+      # You will likely need assistance from the CUDA-Q team to set this up correctly
+      platform-lowering-config: "classical-optimization-pipeline,globalize-array-values,func.func(state-prep),unitary-synthesis,canonicalize,apply-op-specialization,aggressive-early-inlining,classical-optimization-pipeline,func.func(lower-to-cfg,canonicalize,multicontrol-decomposition),decomposition{enable-patterns=SToR1,TToR1,R1ToU3,U3ToRotations,CHToCX,CCZToCX,CRzToCX,CRyToCX,CRxToCX,CR1ToCX},quake-to-cc-prep,func.func(expand-control-veqs,combine-quantum-alloc,canonicalize,combine-measurements),symbol-dce"
+      # Tell the rest-qpu that we are generating OpenQASM 2.0.
+      # As of the time of this writing, qasm2, qir-base and qir-adaptive are supported.
+      codegen-emission: qasm2
+      # Library mode is only for simulators, physical backends must turn this off
+      library-mode: false
+    
+    # Some examples of target arguments are shown below.
+    # You do not need to add any arguments for your backend if you do not need them.
+    target-arguments:
+      - key: api-key
+        required: true
+        type: string
+        platform-arg: api_key
+        help-string: "API key for Provider Name."
+      - key: url
+        required: false
+        type: string
+        platform-arg: url
+        help-string: "Specify Provider Name API server URL."
+      - key: device
+        required: false
+        type: string
+        platform-arg: device
+        help-string: "Specify the Provider Name quantum device to use."
+
+Update Parent ``CMakeLists.txt``
+----------------------------
+
+Add your provider to the parent ``CMakeLists.txt`` file:
+
+.. code-block:: cmake
+
+    # runtime/cudaq/platform/default/rest/helpers/CMakeLists.txt
+    add_subdirectory(<provider_name>)
+    add_subdirectory(ionq)
+    add_subdirectory(iqm)
+    # ... other providers
+
+Testing
+=======
+
+Unit Tests
+----------
+
+Create unit tests for your server helper:
+
+1. Create a directory structure:
+
+.. code-block:: text
+
+    unittests/backends/<provider_name>/
+    ├── CMakeLists.txt
+    ├── ProviderNameStartServerAndTest.sh.in
+    └── ProviderNameTester.cpp
+
+2. Implement the test files:
+
+.. code-block:: cmake
+
+    # CMakeLists.txt
+    add_executable(ProviderNameTester ProviderNameTester.cpp)
+    target_link_libraries(ProviderNameTester
+      PRIVATE
+      cudaq-common
+      cudaq
+      gtest_main
+    )
+    
+    configure_file(${CMAKE_CURRENT_SOURCE_DIR}/ProviderNameStartServerAndTest.sh.in
+                  ${CMAKE_CURRENT_BINARY_DIR}/ProviderNameStartServerAndTest.sh @ONLY)
+    
+    add_test(NAME ProviderNameTester COMMAND ${CMAKE_CURRENT_BINARY_DIR}/ProviderNameStartServerAndTest.sh)
+    set_tests_properties(ProviderNameTester PROPERTIES TIMEOUT 120)
+
+3. Create a shell script to start the mock server and run tests:
+
+.. code-block:: bash
+
+    #!/bin/bash
+    
+    # Start the mock server
+    python3 -m utils.mock_qpu.<provider_name> @PORT@ &
+    SERVER_PID=$!
+    
+    # Wait for server to start
+    sleep 2
+    
+    # Run the test
+    @CMAKE_CURRENT_BINARY_DIR@/ProviderNameTester
+    TEST_STATUS=$?
+    
+    # Kill the server
+    kill $SERVER_PID
+    
+    # Return the test status
+    exit $TEST_STATUS
+
+4. Implement the C++ test:
+
+.. code-block:: cpp
+
+    // ProviderNameTester.cpp
+    #include "common/Logger.h"
+    #include "common/RestClient.h"
+    #include "common/ServerHelper.h"
+    #include "cudaq/platform/quantum_platform.h"
+    #include "gtest/gtest.h"
+    
+    TEST(ProviderNameTester, checkSimpleCircuit) {
+      // Initialize the platform
+      auto platform = cudaq::get_platform();
+      platform->setTargetBackend("<provider_name>");
+      
+      // Set configuration
+      platform->setBackendParameter("url", "http://localhost:PORT");
+      platform->setBackendParameter("api_key", "test_key");
+      
+      // Create a simple circuit
+      auto kernel = cudaq::make_kernel();
+      auto qubits = kernel.qalloc(2);
+      kernel.h(qubits[0]);
+      kernel.cx(qubits[0], qubits[1]);
+      kernel.mz(qubits);
+      
+      // Execute the circuit
+      auto counts = cudaq::sample(kernel);
+      
+      // Check results
+      EXPECT_EQ(counts.size(), 2);
+      EXPECT_TRUE(counts.has_key("00"));
+      EXPECT_TRUE(counts.has_key("11"));
+    }
+
+Mock Server
+----------
+
+Create a mock server for testing:
+
+.. code-block:: text
+
+    utils/mock_qpu/<provider_name>/
+    └── __init__.py
+
+Implement the mock server:
+
+.. code-block:: python
+
+    # __init__.py
+    from http.server import BaseHTTPRequestHandler, HTTPServer
+    import json
+    import sys
+    import time
+    
+    class ProviderNameMockServer(BaseHTTPRequestHandler):
+        def _set_headers(self, status_code=200):
+            self.send_response(status_code)
+            self.send_header('Content-type', 'application/json')
+            self.end_headers()
+    
+        def do_POST(self):
+            content_length = int(self.headers['Content-Length'])
+            post_data = self.rfile.read(content_length)
+            data = json.loads(post_data.decode('utf-8'))
+            
+            if self.path == '/jobs':
+                # Create a job
+                response = {
+                    'id': 'job-123',
+                    'status': 'QUEUED'
+                }
+                self._set_headers()
+                self.wfile.write(json.dumps(response).encode())
+            else:
+                self._set_headers(404)
+                self.wfile.write(json.dumps({'error': 'Not found'}).encode())
+    
+        def do_GET(self):
+            if self.path.startswith('/jobs/job-123'):
+                # Return job status and results
+                response = {
+                    'id': 'job-123',
+                    'status': 'COMPLETED',
+                    'results': {
+                        'counts': {
+                            '00': 500,
+                            '11': 500
+                        }
+                    }
+                }
+                self._set_headers()
+                self.wfile.write(json.dumps(response).encode())
+            else:
+                self._set_headers(404)
+                self.wfile.write(json.dumps({'error': 'Not found'}).encode())
+    
+    def startServer(port=8000):
+        server_address = ('', port)
+        httpd = HTTPServer(server_address, ProviderNameMockServer)
+        print(f'Starting mock server on port {port}...')
+        httpd.serve_forever()
+    
+    if __name__ == '__main__':
+        port = int(sys.argv[1]) if len(sys.argv) > 1 else 8000
+        startServer(port)
+
+Python Tests
+-----------
+
+Create Python tests for your backend:
+
+.. code-block:: python
+
+    # python/tests/backends/test_<provider_name>.py
+    import os
+    import sys
+    import time
+    import pytest
+    from multiprocessing import Process
+    
+    import cudaq
+    from cudaq import spin
+    
+    try:
+        from utils.mock_qpu.<provider_name> import startServer
+    except:
+        print("Mock qpu not available, skipping Provider Name tests.")
+        pytest.skip("Mock qpu not available.", allow_module_level=True)
+    
+    # Define the port for the mock server
+    port = 62444
path: docs/sphinx/using/backends/new_backends.rst
user: login: efratshabtai
body: Is it always the same port or does each QPU needs their own port?
created_at: 2025-05-12T16:19:08Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,528 @@
+*************************
+Adding New Backends
+*************************
+
+This guide explains how to integrate a new quantum hardware provider with CUDA-Q. The integration process involves creating a server helper that handles communication with the provider's API, defining configuration files, and implementing necessary tests.
+
+Overview
+========
+
+CUDA-Q supports various quantum hardware providers through a REST-based architecture. To add a new backend, you'll need to:
+
+1. Create a server helper class that handles API communication
+2. Define configuration files for the target
+3. Implement tests to verify functionality
+4. Add documentation for users
+
+The following sections detail each step of this process.
+
+Server Helper Implementation
+===========================
+
+The server helper is the core component that handles communication with the quantum hardware provider's API. It extends the ``ServerHelper`` base class and implements methods for job submission, result retrieval, and other provider-specific functionality.
+
+Directory Structure
+------------------
+
+Create the following directory structure for your new backend (replace ``<provider_name>`` with your provider's name):
+
+.. code-block:: text
+
+    runtime/cudaq/platform/default/rest/helpers/<provider_name>/
+    ├── CMakeLists.txt
+    ├── ProviderNameServerHelper.cpp
+    └── <provider_name>.yml
+
+Server Helper Class
+------------------
+
+Here's a template for implementing a server helper class:
+
+.. code-block:: cpp
+
+    // ProviderNameServerHelper.cpp
+    #include "common/Logger.h"
+    #include "common/RestClient.h"
+    #include "common/ServerHelper.h"
+    #include "cudaq/Support/Version.h"
+    #include "cudaq/utils/cudaq_utils.h"
+    #include <bitset>
+    #include <fstream>
+    #include <iostream>
+    #include <map>
+    #include <regex>
+    #include <sstream>
+    #include <thread>
+    #include <unordered_set>
+    
+    using json = nlohmann::json;
+    
+    namespace cudaq {
+    
+    /// @brief The ProviderNameServerHelper class extends the ServerHelper class
+    /// to handle interactions with the Provider Name server for submitting and
+    /// retrieving quantum computation jobs.
+    class ProviderNameServerHelper : public ServerHelper {
+      static constexpr const char *DEFAULT_URL = "https://api.provider-name.com";
+      static constexpr const char *DEFAULT_VERSION = "v1.0";
+    
+    public:
+      const std::string name() const override { return "<provider_name>"; }
+    
+      /// @brief Example implementation of authentication headers.
+      RestHeaders getHeaders() override {
+        RestHeaders headers;
+        headers["Content-Type"] = "application/json";
+        
+        // Add authentication headers if needed
+        if (backendConfig.count("api_key"))
+          headers["Authorization"] = "Bearer " + backendConfig["api_key"];
+        
+        return headers;
+      }
+    
+      /// @brief Example implementation of backend initialization.
+      void initialize(BackendConfig config) override {
+        cudaq::info("Initializing Provider Name Backend");
+        backendConfig = config;
+        
+        if (!backendConfig.count("url"))
+          backendConfig["url"] = DEFAULT_URL;
+        if (!backendConfig.count("version"))
+          backendConfig["version"] = DEFAULT_VERSION;
+
+        // Set shots if provided
+        if (config.find("shots") != config.end())
+          this->setShots(std::stoul(config["shots"]));
+      }
+    
+      /// @brief Example implementation of simple job creation.
+      ServerJobPayload createJob(std::vector<KernelExecution> &circuitCodes) override {
+        ServerMessage job;
+        job["content"] = circuitCodes[0].code;
+        job["shots"] = shots;
+        
+        RestHeaders headers = getHeaders();
+        std::string path = "/jobs";
+        
+        return std::make_tuple(backendConfig["url"] + path, headers, 
+                              std::vector<ServerMessage>{job});
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string extractJobId(ServerMessage &postResponse) override {
+        if (!postResponse.contains("id"))
+          return "";
+        
+        return postResponse.at("id");
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string constructGetJobPath(ServerMessage &postResponse) override {
+        return extractJobId(postResponse);
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string constructGetJobPath(std::string &jobId) override {
+        return backendConfig["url"] + "/jobs/" + jobId;
+      }
+    
+      /// @brief Example implementation of job status checking.
+      bool jobIsDone(ServerMessage &getJobResponse) override {
+        if (!getJobResponse.contains("status"))
+          return false;
+        
+        std::string status = getJobResponse["status"];
+        return status == "COMPLETED" || status == "FAILED";
+      }
+    
+      /// @brief Example implementation of result processing.
+      cudaq::sample_result processResults(ServerMessage &getJobResponse,
+                                         std::string &jobId) override {
+        cudaq::info("Processing results: {}", getJobResponse.dump());
+        
+        // Extract measurement results from the response
+        auto samplesJson = getJobResponse["results"]["counts"];
+        cudaq::CountsDictionary counts;
+        
+        for (auto &item : samplesJson.items()) {
+          std::string bitstring = item.key();
+          std::size_t count = item.value();
+          counts[bitstring] = count;
+        }
+        
+        // Create an ExecutionResult
+        cudaq::ExecutionResult execResult{counts};
+        
+        // Return the sample_result
+        return cudaq::sample_result{execResult};
+      }
+    
+      /// @brief Example implementation of polling configuration.
+      std::chrono::microseconds
+      nextResultPollingInterval(ServerMessage &postResponse) override {
+        return std::chrono::seconds(5);
+      }
+    };
+    
+    } // namespace cudaq
+    
+    // Register the server helper in the CUDA-Q server helper factory
+    CUDAQ_REGISTER_TYPE(cudaq::ServerHelper, cudaq::ProviderNameServerHelper, <provider_name>)
+
+``CMakeLists.txt``
+--------------
+
+Create a ``CMakeLists.txt`` file for your server helper:
+
+.. code-block:: cmake
+
+    # CMakeLists.txt
+    target_sources(cudaq-rest-qpu PRIVATE ProviderNameServerHelper.cpp)
+    add_target_config(<provider_name>)
+    
+    add_library(cudaq-serverhelper-<provider_name> SHARED ProviderNameServerHelper.cpp)
+    target_link_libraries(cudaq-serverhelper-<provider_name>
+      PUBLIC
+      cudaq-common
+      fmt::fmt-header-only
+    )
+    install(TARGETS cudaq-serverhelper-<provider_name> DESTINATION lib)
+
+Target Configuration
+===================
+
+Create a ``YAML`` configuration file for your target:
+
+.. code-block:: yaml
+
+    # <provider_name>.yml
+    name: "<provider_name>"
+    description: "CUDA-Q target for Provider Name."
+    
+    config:
+      # Tell DefaultQuantumPlatform what QPU subtype to use
+      platform-qpu: remote_rest
+      # Add the rest-qpu library to the link list
+      link-libs: ["-lcudaq-rest-qpu"]
+      # Tell NVQ++ to generate glue code to set the target backend name
+      gen-target-backend: true
+      # Add preprocessor defines to compilation
+      preprocessor-defines: ["-D CUDAQ_QUANTUM_DEVICE"]
+      # Define the lowering pipeline
+      # You will likely need assistance from the CUDA-Q team to set this up correctly
+      platform-lowering-config: "classical-optimization-pipeline,globalize-array-values,func.func(state-prep),unitary-synthesis,canonicalize,apply-op-specialization,aggressive-early-inlining,classical-optimization-pipeline,func.func(lower-to-cfg,canonicalize,multicontrol-decomposition),decomposition{enable-patterns=SToR1,TToR1,R1ToU3,U3ToRotations,CHToCX,CCZToCX,CRzToCX,CRyToCX,CRxToCX,CR1ToCX},quake-to-cc-prep,func.func(expand-control-veqs,combine-quantum-alloc,canonicalize,combine-measurements),symbol-dce"
+      # Tell the rest-qpu that we are generating OpenQASM 2.0.
+      # As of the time of this writing, qasm2, qir-base and qir-adaptive are supported.
+      codegen-emission: qasm2
+      # Library mode is only for simulators, physical backends must turn this off
+      library-mode: false
+    
+    # Some examples of target arguments are shown below.
+    # You do not need to add any arguments for your backend if you do not need them.
+    target-arguments:
+      - key: api-key
+        required: true
+        type: string
+        platform-arg: api_key
+        help-string: "API key for Provider Name."
+      - key: url
+        required: false
+        type: string
+        platform-arg: url
+        help-string: "Specify Provider Name API server URL."
+      - key: device
+        required: false
+        type: string
+        platform-arg: device
+        help-string: "Specify the Provider Name quantum device to use."
+
+Update Parent ``CMakeLists.txt``
+----------------------------
+
+Add your provider to the parent ``CMakeLists.txt`` file:
+
+.. code-block:: cmake
+
+    # runtime/cudaq/platform/default/rest/helpers/CMakeLists.txt
+    add_subdirectory(<provider_name>)
+    add_subdirectory(ionq)
+    add_subdirectory(iqm)
+    # ... other providers
+
+Testing
+=======
+
+Unit Tests
+----------
+
+Create unit tests for your server helper:
+
+1. Create a directory structure:
+
+.. code-block:: text
+
+    unittests/backends/<provider_name>/
+    ├── CMakeLists.txt
+    ├── ProviderNameStartServerAndTest.sh.in
+    └── ProviderNameTester.cpp
+
+2. Implement the test files:
+
+.. code-block:: cmake
+
+    # CMakeLists.txt
+    add_executable(ProviderNameTester ProviderNameTester.cpp)
+    target_link_libraries(ProviderNameTester
+      PRIVATE
+      cudaq-common
+      cudaq
+      gtest_main
+    )
+    
+    configure_file(${CMAKE_CURRENT_SOURCE_DIR}/ProviderNameStartServerAndTest.sh.in
+                  ${CMAKE_CURRENT_BINARY_DIR}/ProviderNameStartServerAndTest.sh @ONLY)
+    
+    add_test(NAME ProviderNameTester COMMAND ${CMAKE_CURRENT_BINARY_DIR}/ProviderNameStartServerAndTest.sh)
+    set_tests_properties(ProviderNameTester PROPERTIES TIMEOUT 120)
+
+3. Create a shell script to start the mock server and run tests:
+
+.. code-block:: bash
+
+    #!/bin/bash
+    
+    # Start the mock server
+    python3 -m utils.mock_qpu.<provider_name> @PORT@ &
+    SERVER_PID=$!
+    
+    # Wait for server to start
+    sleep 2
+    
+    # Run the test
+    @CMAKE_CURRENT_BINARY_DIR@/ProviderNameTester
+    TEST_STATUS=$?
+    
+    # Kill the server
+    kill $SERVER_PID
+    
+    # Return the test status
+    exit $TEST_STATUS
+
+4. Implement the C++ test:
+
+.. code-block:: cpp
+
+    // ProviderNameTester.cpp
+    #include "common/Logger.h"
+    #include "common/RestClient.h"
+    #include "common/ServerHelper.h"
+    #include "cudaq/platform/quantum_platform.h"
+    #include "gtest/gtest.h"
+    
+    TEST(ProviderNameTester, checkSimpleCircuit) {
+      // Initialize the platform
+      auto platform = cudaq::get_platform();
+      platform->setTargetBackend("<provider_name>");
+      
+      // Set configuration
+      platform->setBackendParameter("url", "http://localhost:PORT");
+      platform->setBackendParameter("api_key", "test_key");
+      
+      // Create a simple circuit
+      auto kernel = cudaq::make_kernel();
+      auto qubits = kernel.qalloc(2);
+      kernel.h(qubits[0]);
+      kernel.cx(qubits[0], qubits[1]);
+      kernel.mz(qubits);
+      
+      // Execute the circuit
+      auto counts = cudaq::sample(kernel);
+      
+      // Check results
+      EXPECT_EQ(counts.size(), 2);
+      EXPECT_TRUE(counts.has_key("00"));
+      EXPECT_TRUE(counts.has_key("11"));
+    }
+
+Mock Server
+----------
+
+Create a mock server for testing:
+
+.. code-block:: text
+
+    utils/mock_qpu/<provider_name>/
+    └── __init__.py
+
+Implement the mock server:
+
+.. code-block:: python
+
+    # __init__.py
+    from http.server import BaseHTTPRequestHandler, HTTPServer
+    import json
+    import sys
+    import time
+    
+    class ProviderNameMockServer(BaseHTTPRequestHandler):
+        def _set_headers(self, status_code=200):
+            self.send_response(status_code)
+            self.send_header('Content-type', 'application/json')
+            self.end_headers()
+    
+        def do_POST(self):
+            content_length = int(self.headers['Content-Length'])
+            post_data = self.rfile.read(content_length)
+            data = json.loads(post_data.decode('utf-8'))
+            
+            if self.path == '/jobs':
+                # Create a job
+                response = {
+                    'id': 'job-123',
+                    'status': 'QUEUED'
+                }
+                self._set_headers()
+                self.wfile.write(json.dumps(response).encode())
+            else:
+                self._set_headers(404)
+                self.wfile.write(json.dumps({'error': 'Not found'}).encode())
+    
+        def do_GET(self):
+            if self.path.startswith('/jobs/job-123'):
+                # Return job status and results
+                response = {
+                    'id': 'job-123',
+                    'status': 'COMPLETED',
+                    'results': {
+                        'counts': {
+                            '00': 500,
+                            '11': 500
+                        }
+                    }
+                }
+                self._set_headers()
+                self.wfile.write(json.dumps(response).encode())
+            else:
+                self._set_headers(404)
+                self.wfile.write(json.dumps({'error': 'Not found'}).encode())
+    
+    def startServer(port=8000):
+        server_address = ('', port)
+        httpd = HTTPServer(server_address, ProviderNameMockServer)
+        print(f'Starting mock server on port {port}...')
+        httpd.serve_forever()
+    
+    if __name__ == '__main__':
+        port = int(sys.argv[1]) if len(sys.argv) > 1 else 8000
+        startServer(port)
+
+Python Tests
+-----------
+
+Create Python tests for your backend:
+
+.. code-block:: python
+
+    # python/tests/backends/test_<provider_name>.py
+    import os
+    import sys
+    import time
+    import pytest
+    from multiprocessing import Process
+    
+    import cudaq
+    from cudaq import spin
+    
+    try:
+        from utils.mock_qpu.<provider_name> import startServer
+    except:
+        print("Mock qpu not available, skipping Provider Name 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():
+        # Set the targeted QPU
+        cudaq.set_target('<provider_name>',
+                        url=f'http://localhost:{port}',
+                        api_key="test_key")
+        
+        # Launch the Mock Server
+        p = Process(target=startServer, args=(port,))
+        p.start()
+        time.sleep(1)
+        
+        yield "Running the tests."
+        
+        # Kill the server
+        p.terminate()
+    
+    def test_<provider_name>_sample():
+        # Create the kernel
+        kernel = cudaq.make_kernel()
+        qubits = kernel.qalloc(2)
+        kernel.h(qubits[0])
+        kernel.cx(qubits[0], qubits[1])
+        kernel.mz(qubits)
+        
+        # Run sample
+        counts = cudaq.sample(kernel)
+        assert len(counts) == 2
+        assert '00' in counts
+        assert '11' in counts
+        
+        # Run sample asynchronously
+        future = cudaq.sample_async(kernel)
+        counts = future.get()
+        assert len(counts) == 2
+        assert '00' in counts
+        assert '11' in counts
+
+Documentation
+============
+
+Add documentation for your backend in the appropriate sections of the CUDA-Q documentation. This should include:
+
+1. How to configure and use the backend
+2. Any provider-specific parameters or features
+3. Examples of running circuits on the backend
+
path: docs/sphinx/using/backends/new_backends.rst
user: login: efratshabtai
body: @mawolf2023 any requirements on adding the icon to the backends [diagram](https://nvidia.github.io/cuda-quantum/latest/using/backends/hardware.html) / list ? (Alphabetical order per modality, specific icon format, etc. )
created_at: 2025-05-12T16:22:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,528 @@
+*************************
+Adding New Backends
+*************************
+
+This guide explains how to integrate a new quantum hardware provider with CUDA-Q. The integration process involves creating a server helper that handles communication with the provider's API, defining configuration files, and implementing necessary tests.
+
+Overview
+========
+
+CUDA-Q supports various quantum hardware providers through a REST-based architecture. To add a new backend, you'll need to:
+
+1. Create a server helper class that handles API communication
+2. Define configuration files for the target
+3. Implement tests to verify functionality
+4. Add documentation for users
+
+The following sections detail each step of this process.
+
+Server Helper Implementation
+===========================
+
+The server helper is the core component that handles communication with the quantum hardware provider's API. It extends the ``ServerHelper`` base class and implements methods for job submission, result retrieval, and other provider-specific functionality.
+
+Directory Structure
+------------------
+
+Create the following directory structure for your new backend (replace ``<provider_name>`` with your provider's name):
+
+.. code-block:: text
+
+    runtime/cudaq/platform/default/rest/helpers/<provider_name>/
+    ├── CMakeLists.txt
+    ├── ProviderNameServerHelper.cpp
+    └── <provider_name>.yml
+
+Server Helper Class
+------------------
+
+Here's a template for implementing a server helper class:
+
+.. code-block:: cpp
+
+    // ProviderNameServerHelper.cpp
+    #include "common/Logger.h"
+    #include "common/RestClient.h"
+    #include "common/ServerHelper.h"
+    #include "cudaq/Support/Version.h"
+    #include "cudaq/utils/cudaq_utils.h"
+    #include <bitset>
+    #include <fstream>
+    #include <iostream>
+    #include <map>
+    #include <regex>
+    #include <sstream>
+    #include <thread>
+    #include <unordered_set>
+    
+    using json = nlohmann::json;
+    
+    namespace cudaq {
+    
+    /// @brief The ProviderNameServerHelper class extends the ServerHelper class
+    /// to handle interactions with the Provider Name server for submitting and
+    /// retrieving quantum computation jobs.
+    class ProviderNameServerHelper : public ServerHelper {
+      static constexpr const char *DEFAULT_URL = "https://api.provider-name.com";
+      static constexpr const char *DEFAULT_VERSION = "v1.0";
+    
+    public:
+      const std::string name() const override { return "<provider_name>"; }
+    
+      /// @brief Example implementation of authentication headers.
+      RestHeaders getHeaders() override {
+        RestHeaders headers;
+        headers["Content-Type"] = "application/json";
+        
+        // Add authentication headers if needed
+        if (backendConfig.count("api_key"))
+          headers["Authorization"] = "Bearer " + backendConfig["api_key"];
+        
+        return headers;
+      }
+    
+      /// @brief Example implementation of backend initialization.
+      void initialize(BackendConfig config) override {
+        cudaq::info("Initializing Provider Name Backend");
+        backendConfig = config;
+        
+        if (!backendConfig.count("url"))
+          backendConfig["url"] = DEFAULT_URL;
+        if (!backendConfig.count("version"))
+          backendConfig["version"] = DEFAULT_VERSION;
+
+        // Set shots if provided
+        if (config.find("shots") != config.end())
+          this->setShots(std::stoul(config["shots"]));
+      }
+    
+      /// @brief Example implementation of simple job creation.
+      ServerJobPayload createJob(std::vector<KernelExecution> &circuitCodes) override {
+        ServerMessage job;
+        job["content"] = circuitCodes[0].code;
+        job["shots"] = shots;
+        
+        RestHeaders headers = getHeaders();
+        std::string path = "/jobs";
+        
+        return std::make_tuple(backendConfig["url"] + path, headers, 
+                              std::vector<ServerMessage>{job});
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string extractJobId(ServerMessage &postResponse) override {
+        if (!postResponse.contains("id"))
+          return "";
+        
+        return postResponse.at("id");
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string constructGetJobPath(ServerMessage &postResponse) override {
+        return extractJobId(postResponse);
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string constructGetJobPath(std::string &jobId) override {
+        return backendConfig["url"] + "/jobs/" + jobId;
+      }
+    
+      /// @brief Example implementation of job status checking.
+      bool jobIsDone(ServerMessage &getJobResponse) override {
+        if (!getJobResponse.contains("status"))
+          return false;
+        
+        std::string status = getJobResponse["status"];
+        return status == "COMPLETED" || status == "FAILED";
+      }
+    
+      /// @brief Example implementation of result processing.
+      cudaq::sample_result processResults(ServerMessage &getJobResponse,
+                                         std::string &jobId) override {
+        cudaq::info("Processing results: {}", getJobResponse.dump());
+        
+        // Extract measurement results from the response
+        auto samplesJson = getJobResponse["results"]["counts"];
+        cudaq::CountsDictionary counts;
+        
+        for (auto &item : samplesJson.items()) {
+          std::string bitstring = item.key();
+          std::size_t count = item.value();
+          counts[bitstring] = count;
+        }
+        
+        // Create an ExecutionResult
+        cudaq::ExecutionResult execResult{counts};
+        
+        // Return the sample_result
+        return cudaq::sample_result{execResult};
+      }
+    
+      /// @brief Example implementation of polling configuration.
+      std::chrono::microseconds
+      nextResultPollingInterval(ServerMessage &postResponse) override {
+        return std::chrono::seconds(5);
+      }
+    };
+    
+    } // namespace cudaq
+    
+    // Register the server helper in the CUDA-Q server helper factory
+    CUDAQ_REGISTER_TYPE(cudaq::ServerHelper, cudaq::ProviderNameServerHelper, <provider_name>)
+
+``CMakeLists.txt``
+--------------
+
+Create a ``CMakeLists.txt`` file for your server helper:
+
+.. code-block:: cmake
+
+    # CMakeLists.txt
+    target_sources(cudaq-rest-qpu PRIVATE ProviderNameServerHelper.cpp)
+    add_target_config(<provider_name>)
+    
+    add_library(cudaq-serverhelper-<provider_name> SHARED ProviderNameServerHelper.cpp)
+    target_link_libraries(cudaq-serverhelper-<provider_name>
+      PUBLIC
+      cudaq-common
+      fmt::fmt-header-only
+    )
+    install(TARGETS cudaq-serverhelper-<provider_name> DESTINATION lib)
+
+Target Configuration
+===================
+
+Create a ``YAML`` configuration file for your target:
+
+.. code-block:: yaml
+
+    # <provider_name>.yml
+    name: "<provider_name>"
+    description: "CUDA-Q target for Provider Name."
+    
+    config:
+      # Tell DefaultQuantumPlatform what QPU subtype to use
+      platform-qpu: remote_rest
+      # Add the rest-qpu library to the link list
+      link-libs: ["-lcudaq-rest-qpu"]
+      # Tell NVQ++ to generate glue code to set the target backend name
+      gen-target-backend: true
+      # Add preprocessor defines to compilation
+      preprocessor-defines: ["-D CUDAQ_QUANTUM_DEVICE"]
+      # Define the lowering pipeline
+      # You will likely need assistance from the CUDA-Q team to set this up correctly
+      platform-lowering-config: "classical-optimization-pipeline,globalize-array-values,func.func(state-prep),unitary-synthesis,canonicalize,apply-op-specialization,aggressive-early-inlining,classical-optimization-pipeline,func.func(lower-to-cfg,canonicalize,multicontrol-decomposition),decomposition{enable-patterns=SToR1,TToR1,R1ToU3,U3ToRotations,CHToCX,CCZToCX,CRzToCX,CRyToCX,CRxToCX,CR1ToCX},quake-to-cc-prep,func.func(expand-control-veqs,combine-quantum-alloc,canonicalize,combine-measurements),symbol-dce"
+      # Tell the rest-qpu that we are generating OpenQASM 2.0.
+      # As of the time of this writing, qasm2, qir-base and qir-adaptive are supported.
+      codegen-emission: qasm2
+      # Library mode is only for simulators, physical backends must turn this off
+      library-mode: false
+    
+    # Some examples of target arguments are shown below.
+    # You do not need to add any arguments for your backend if you do not need them.
+    target-arguments:
+      - key: api-key
+        required: true
+        type: string
+        platform-arg: api_key
+        help-string: "API key for Provider Name."
+      - key: url
+        required: false
+        type: string
+        platform-arg: url
+        help-string: "Specify Provider Name API server URL."
+      - key: device
+        required: false
+        type: string
+        platform-arg: device
+        help-string: "Specify the Provider Name quantum device to use."
+
+Update Parent ``CMakeLists.txt``
+----------------------------
+
+Add your provider to the parent ``CMakeLists.txt`` file:
+
+.. code-block:: cmake
+
+    # runtime/cudaq/platform/default/rest/helpers/CMakeLists.txt
+    add_subdirectory(<provider_name>)
+    add_subdirectory(ionq)
+    add_subdirectory(iqm)
+    # ... other providers
+
+Testing
+=======
+
+Unit Tests
+----------
+
+Create unit tests for your server helper:
+
+1. Create a directory structure:
+
+.. code-block:: text
+
+    unittests/backends/<provider_name>/
+    ├── CMakeLists.txt
+    ├── ProviderNameStartServerAndTest.sh.in
+    └── ProviderNameTester.cpp
+
+2. Implement the test files:
+
+.. code-block:: cmake
+
+    # CMakeLists.txt
+    add_executable(ProviderNameTester ProviderNameTester.cpp)
+    target_link_libraries(ProviderNameTester
+      PRIVATE
+      cudaq-common
+      cudaq
+      gtest_main
+    )
+    
+    configure_file(${CMAKE_CURRENT_SOURCE_DIR}/ProviderNameStartServerAndTest.sh.in
+                  ${CMAKE_CURRENT_BINARY_DIR}/ProviderNameStartServerAndTest.sh @ONLY)
+    
+    add_test(NAME ProviderNameTester COMMAND ${CMAKE_CURRENT_BINARY_DIR}/ProviderNameStartServerAndTest.sh)
+    set_tests_properties(ProviderNameTester PROPERTIES TIMEOUT 120)
+
+3. Create a shell script to start the mock server and run tests:
+
+.. code-block:: bash
+
+    #!/bin/bash
+    
+    # Start the mock server
+    python3 -m utils.mock_qpu.<provider_name> @PORT@ &
+    SERVER_PID=$!
+    
+    # Wait for server to start
+    sleep 2
+    
+    # Run the test
+    @CMAKE_CURRENT_BINARY_DIR@/ProviderNameTester
+    TEST_STATUS=$?
+    
+    # Kill the server
+    kill $SERVER_PID
+    
+    # Return the test status
+    exit $TEST_STATUS
+
+4. Implement the C++ test:
+
+.. code-block:: cpp
+
+    // ProviderNameTester.cpp
+    #include "common/Logger.h"
+    #include "common/RestClient.h"
+    #include "common/ServerHelper.h"
+    #include "cudaq/platform/quantum_platform.h"
+    #include "gtest/gtest.h"
+    
+    TEST(ProviderNameTester, checkSimpleCircuit) {
+      // Initialize the platform
+      auto platform = cudaq::get_platform();
+      platform->setTargetBackend("<provider_name>");
+      
+      // Set configuration
+      platform->setBackendParameter("url", "http://localhost:PORT");
+      platform->setBackendParameter("api_key", "test_key");
+      
+      // Create a simple circuit
+      auto kernel = cudaq::make_kernel();
+      auto qubits = kernel.qalloc(2);
+      kernel.h(qubits[0]);
+      kernel.cx(qubits[0], qubits[1]);
+      kernel.mz(qubits);
+      
+      // Execute the circuit
+      auto counts = cudaq::sample(kernel);
+      
+      // Check results
+      EXPECT_EQ(counts.size(), 2);
+      EXPECT_TRUE(counts.has_key("00"));
+      EXPECT_TRUE(counts.has_key("11"));
+    }
+
+Mock Server
+----------
+
+Create a mock server for testing:
+
+.. code-block:: text
+
+    utils/mock_qpu/<provider_name>/
+    └── __init__.py
+
+Implement the mock server:
+
+.. code-block:: python
+
+    # __init__.py
+    from http.server import BaseHTTPRequestHandler, HTTPServer
+    import json
+    import sys
+    import time
+    
+    class ProviderNameMockServer(BaseHTTPRequestHandler):
+        def _set_headers(self, status_code=200):
+            self.send_response(status_code)
+            self.send_header('Content-type', 'application/json')
+            self.end_headers()
+    
+        def do_POST(self):
+            content_length = int(self.headers['Content-Length'])
+            post_data = self.rfile.read(content_length)
+            data = json.loads(post_data.decode('utf-8'))
+            
+            if self.path == '/jobs':
+                # Create a job
+                response = {
+                    'id': 'job-123',
+                    'status': 'QUEUED'
+                }
+                self._set_headers()
+                self.wfile.write(json.dumps(response).encode())
+            else:
+                self._set_headers(404)
+                self.wfile.write(json.dumps({'error': 'Not found'}).encode())
+    
+        def do_GET(self):
+            if self.path.startswith('/jobs/job-123'):
+                # Return job status and results
+                response = {
+                    'id': 'job-123',
+                    'status': 'COMPLETED',
+                    'results': {
+                        'counts': {
+                            '00': 500,
+                            '11': 500
+                        }
+                    }
+                }
+                self._set_headers()
+                self.wfile.write(json.dumps(response).encode())
+            else:
+                self._set_headers(404)
+                self.wfile.write(json.dumps({'error': 'Not found'}).encode())
+    
+    def startServer(port=8000):
+        server_address = ('', port)
+        httpd = HTTPServer(server_address, ProviderNameMockServer)
+        print(f'Starting mock server on port {port}...')
+        httpd.serve_forever()
+    
+    if __name__ == '__main__':
+        port = int(sys.argv[1]) if len(sys.argv) > 1 else 8000
+        startServer(port)
+
+Python Tests
+-----------
+
+Create Python tests for your backend:
+
+.. code-block:: python
+
+    # python/tests/backends/test_<provider_name>.py
+    import os
+    import sys
+    import time
+    import pytest
+    from multiprocessing import Process
+    
+    import cudaq
+    from cudaq import spin
+    
+    try:
+        from utils.mock_qpu.<provider_name> import startServer
+    except:
+        print("Mock qpu not available, skipping Provider Name 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():
+        # Set the targeted QPU
+        cudaq.set_target('<provider_name>',
+                        url=f'http://localhost:{port}',
+                        api_key="test_key")
+        
+        # Launch the Mock Server
+        p = Process(target=startServer, args=(port,))
+        p.start()
+        time.sleep(1)
+        
+        yield "Running the tests."
+        
+        # Kill the server
+        p.terminate()
+    
+    def test_<provider_name>_sample():
+        # Create the kernel
+        kernel = cudaq.make_kernel()
+        qubits = kernel.qalloc(2)
+        kernel.h(qubits[0])
+        kernel.cx(qubits[0], qubits[1])
+        kernel.mz(qubits)
+        
+        # Run sample
+        counts = cudaq.sample(kernel)
+        assert len(counts) == 2
+        assert '00' in counts
+        assert '11' in counts
+        
+        # Run sample asynchronously
+        future = cudaq.sample_async(kernel)
+        counts = future.get()
+        assert len(counts) == 2
+        assert '00' in counts
+        assert '11' in counts
+
+Documentation
+============
+
+Add documentation for your backend in the appropriate sections of the CUDA-Q documentation. This should include:
+
+1. How to configure and use the backend
+2. Any provider-specific parameters or features
+3. Examples of running circuits on the backend
+
+More specifically, you will need to modify at least the following files:
+* ``docs/sphinx/using/examples/hardware_providers.rst``
+* ``docs/sphinx/using/backends/hardware/<your-technology>.rst``
+* ``docs/sphinx/targets/python/<provider_name>.py``
+* ``docs/sphinx/targets/cpp/<provider_name>.cpp``
+
+Example Usage
+============
+
+Once your backend is implemented, users can use it as follows:
+
+.. code-block:: python
+
+    import cudaq
+    
+    # Set the target to your provider
+    cudaq.set_target('<provider_name>', 
+                    api_key='your_api_key',
+                    device='your_device')
+    
+    # Create and run a circuit
+    @cudaq.kernel
+    def bell():
+        qubits = cudaq.qvector(2)
+        h(qubits[0])
+        x.ctrl(qubits[0], qubits[1])
+        mz(qubits)
+    
+    # Run the circuit
+    counts = cudaq.sample(bell)
+    print(counts)
+
+Conclusion
path: docs/sphinx/using/backends/new_backends.rst
user: login: efratshabtai
body: Let's add a section of maintenance: we want the integration to continue to work with new CUDA-Q versions and new hardware systems - what is under the CUDA-Q team responsibility? what is under the responsibility of the QPU provider?
created_at: 2025-05-12T16:24:40Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,528 @@
+*************************
+Adding New Backends
+*************************
+
+This guide explains how to integrate a new quantum hardware provider with CUDA-Q. The integration process involves creating a server helper that handles communication with the provider's API, defining configuration files, and implementing necessary tests.
+
+Overview
+========
+
+CUDA-Q supports various quantum hardware providers through a REST-based architecture. To add a new backend, you'll need to:
+
+1. Create a server helper class that handles API communication
+2. Define configuration files for the target
+3. Implement tests to verify functionality
+4. Add documentation for users
+
+The following sections detail each step of this process.
+
+Server Helper Implementation
+===========================
+
+The server helper is the core component that handles communication with the quantum hardware provider's API. It extends the ``ServerHelper`` base class and implements methods for job submission, result retrieval, and other provider-specific functionality.
+
+Directory Structure
+------------------
+
+Create the following directory structure for your new backend (replace ``<provider_name>`` with your provider's name):
+
+.. code-block:: text
+
+    runtime/cudaq/platform/default/rest/helpers/<provider_name>/
+    ├── CMakeLists.txt
+    ├── ProviderNameServerHelper.cpp
+    └── <provider_name>.yml
+
+Server Helper Class
+------------------
+
+Here's a template for implementing a server helper class:
+
+.. code-block:: cpp
+
+    // ProviderNameServerHelper.cpp
+    #include "common/Logger.h"
+    #include "common/RestClient.h"
+    #include "common/ServerHelper.h"
+    #include "cudaq/Support/Version.h"
+    #include "cudaq/utils/cudaq_utils.h"
+    #include <bitset>
+    #include <fstream>
+    #include <iostream>
+    #include <map>
+    #include <regex>
+    #include <sstream>
+    #include <thread>
+    #include <unordered_set>
+    
+    using json = nlohmann::json;
+    
+    namespace cudaq {
+    
+    /// @brief The ProviderNameServerHelper class extends the ServerHelper class
+    /// to handle interactions with the Provider Name server for submitting and
+    /// retrieving quantum computation jobs.
+    class ProviderNameServerHelper : public ServerHelper {
+      static constexpr const char *DEFAULT_URL = "https://api.provider-name.com";
+      static constexpr const char *DEFAULT_VERSION = "v1.0";
+    
+    public:
+      const std::string name() const override { return "<provider_name>"; }
+    
+      /// @brief Example implementation of authentication headers.
+      RestHeaders getHeaders() override {
+        RestHeaders headers;
+        headers["Content-Type"] = "application/json";
+        
+        // Add authentication headers if needed
+        if (backendConfig.count("api_key"))
+          headers["Authorization"] = "Bearer " + backendConfig["api_key"];
+        
+        return headers;
+      }
+    
+      /// @brief Example implementation of backend initialization.
+      void initialize(BackendConfig config) override {
+        cudaq::info("Initializing Provider Name Backend");
+        backendConfig = config;
+        
+        if (!backendConfig.count("url"))
+          backendConfig["url"] = DEFAULT_URL;
+        if (!backendConfig.count("version"))
+          backendConfig["version"] = DEFAULT_VERSION;
+
+        // Set shots if provided
+        if (config.find("shots") != config.end())
+          this->setShots(std::stoul(config["shots"]));
+      }
+    
+      /// @brief Example implementation of simple job creation.
+      ServerJobPayload createJob(std::vector<KernelExecution> &circuitCodes) override {
+        ServerMessage job;
+        job["content"] = circuitCodes[0].code;
+        job["shots"] = shots;
+        
+        RestHeaders headers = getHeaders();
+        std::string path = "/jobs";
+        
+        return std::make_tuple(backendConfig["url"] + path, headers, 
+                              std::vector<ServerMessage>{job});
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string extractJobId(ServerMessage &postResponse) override {
+        if (!postResponse.contains("id"))
+          return "";
+        
+        return postResponse.at("id");
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string constructGetJobPath(ServerMessage &postResponse) override {
+        return extractJobId(postResponse);
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string constructGetJobPath(std::string &jobId) override {
+        return backendConfig["url"] + "/jobs/" + jobId;
+      }
+    
+      /// @brief Example implementation of job status checking.
+      bool jobIsDone(ServerMessage &getJobResponse) override {
+        if (!getJobResponse.contains("status"))
+          return false;
+        
+        std::string status = getJobResponse["status"];
+        return status == "COMPLETED" || status == "FAILED";
+      }
+    
+      /// @brief Example implementation of result processing.
+      cudaq::sample_result processResults(ServerMessage &getJobResponse,
+                                         std::string &jobId) override {
+        cudaq::info("Processing results: {}", getJobResponse.dump());
+        
+        // Extract measurement results from the response
+        auto samplesJson = getJobResponse["results"]["counts"];
+        cudaq::CountsDictionary counts;
+        
+        for (auto &item : samplesJson.items()) {
+          std::string bitstring = item.key();
+          std::size_t count = item.value();
+          counts[bitstring] = count;
+        }
+        
+        // Create an ExecutionResult
+        cudaq::ExecutionResult execResult{counts};
+        
+        // Return the sample_result
+        return cudaq::sample_result{execResult};
+      }
+    
+      /// @brief Example implementation of polling configuration.
+      std::chrono::microseconds
+      nextResultPollingInterval(ServerMessage &postResponse) override {
+        return std::chrono::seconds(5);
+      }
+    };
+    
+    } // namespace cudaq
+    
+    // Register the server helper in the CUDA-Q server helper factory
+    CUDAQ_REGISTER_TYPE(cudaq::ServerHelper, cudaq::ProviderNameServerHelper, <provider_name>)
+
+``CMakeLists.txt``
+--------------
+
+Create a ``CMakeLists.txt`` file for your server helper:
+
+.. code-block:: cmake
+
+    # CMakeLists.txt
+    target_sources(cudaq-rest-qpu PRIVATE ProviderNameServerHelper.cpp)
+    add_target_config(<provider_name>)
+    
+    add_library(cudaq-serverhelper-<provider_name> SHARED ProviderNameServerHelper.cpp)
+    target_link_libraries(cudaq-serverhelper-<provider_name>
+      PUBLIC
+      cudaq-common
+      fmt::fmt-header-only
+    )
+    install(TARGETS cudaq-serverhelper-<provider_name> DESTINATION lib)
+
+Target Configuration
+===================
+
+Create a ``YAML`` configuration file for your target:
+
+.. code-block:: yaml
+
+    # <provider_name>.yml
+    name: "<provider_name>"
+    description: "CUDA-Q target for Provider Name."
+    
+    config:
+      # Tell DefaultQuantumPlatform what QPU subtype to use
+      platform-qpu: remote_rest
+      # Add the rest-qpu library to the link list
+      link-libs: ["-lcudaq-rest-qpu"]
+      # Tell NVQ++ to generate glue code to set the target backend name
+      gen-target-backend: true
+      # Add preprocessor defines to compilation
+      preprocessor-defines: ["-D CUDAQ_QUANTUM_DEVICE"]
+      # Define the lowering pipeline
+      # You will likely need assistance from the CUDA-Q team to set this up correctly
+      platform-lowering-config: "classical-optimization-pipeline,globalize-array-values,func.func(state-prep),unitary-synthesis,canonicalize,apply-op-specialization,aggressive-early-inlining,classical-optimization-pipeline,func.func(lower-to-cfg,canonicalize,multicontrol-decomposition),decomposition{enable-patterns=SToR1,TToR1,R1ToU3,U3ToRotations,CHToCX,CCZToCX,CRzToCX,CRyToCX,CRxToCX,CR1ToCX},quake-to-cc-prep,func.func(expand-control-veqs,combine-quantum-alloc,canonicalize,combine-measurements),symbol-dce"
+      # Tell the rest-qpu that we are generating OpenQASM 2.0.
+      # As of the time of this writing, qasm2, qir-base and qir-adaptive are supported.
+      codegen-emission: qasm2
+      # Library mode is only for simulators, physical backends must turn this off
+      library-mode: false
+    
+    # Some examples of target arguments are shown below.
+    # You do not need to add any arguments for your backend if you do not need them.
+    target-arguments:
+      - key: api-key
+        required: true
+        type: string
+        platform-arg: api_key
+        help-string: "API key for Provider Name."
+      - key: url
+        required: false
+        type: string
+        platform-arg: url
+        help-string: "Specify Provider Name API server URL."
+      - key: device
+        required: false
+        type: string
+        platform-arg: device
+        help-string: "Specify the Provider Name quantum device to use."
+
+Update Parent ``CMakeLists.txt``
+----------------------------
+
+Add your provider to the parent ``CMakeLists.txt`` file:
+
+.. code-block:: cmake
+
+    # runtime/cudaq/platform/default/rest/helpers/CMakeLists.txt
+    add_subdirectory(<provider_name>)
+    add_subdirectory(ionq)
+    add_subdirectory(iqm)
+    # ... other providers
+
+Testing
+=======
+
+Unit Tests
+----------
+
+Create unit tests for your server helper:
+
+1. Create a directory structure:
+
+.. code-block:: text
+
+    unittests/backends/<provider_name>/
+    ├── CMakeLists.txt
+    ├── ProviderNameStartServerAndTest.sh.in
+    └── ProviderNameTester.cpp
+
+2. Implement the test files:
+
+.. code-block:: cmake
+
+    # CMakeLists.txt
+    add_executable(ProviderNameTester ProviderNameTester.cpp)
+    target_link_libraries(ProviderNameTester
+      PRIVATE
+      cudaq-common
+      cudaq
+      gtest_main
+    )
+    
+    configure_file(${CMAKE_CURRENT_SOURCE_DIR}/ProviderNameStartServerAndTest.sh.in
+                  ${CMAKE_CURRENT_BINARY_DIR}/ProviderNameStartServerAndTest.sh @ONLY)
+    
+    add_test(NAME ProviderNameTester COMMAND ${CMAKE_CURRENT_BINARY_DIR}/ProviderNameStartServerAndTest.sh)
+    set_tests_properties(ProviderNameTester PROPERTIES TIMEOUT 120)
+
+3. Create a shell script to start the mock server and run tests:
+
+.. code-block:: bash
+
+    #!/bin/bash
+    
+    # Start the mock server
+    python3 -m utils.mock_qpu.<provider_name> @PORT@ &
+    SERVER_PID=$!
+    
+    # Wait for server to start
+    sleep 2
+    
+    # Run the test
+    @CMAKE_CURRENT_BINARY_DIR@/ProviderNameTester
+    TEST_STATUS=$?
+    
+    # Kill the server
+    kill $SERVER_PID
+    
+    # Return the test status
+    exit $TEST_STATUS
+
+4. Implement the C++ test:
+
+.. code-block:: cpp
+
+    // ProviderNameTester.cpp
+    #include "common/Logger.h"
+    #include "common/RestClient.h"
+    #include "common/ServerHelper.h"
+    #include "cudaq/platform/quantum_platform.h"
+    #include "gtest/gtest.h"
+    
+    TEST(ProviderNameTester, checkSimpleCircuit) {
+      // Initialize the platform
+      auto platform = cudaq::get_platform();
+      platform->setTargetBackend("<provider_name>");
+      
+      // Set configuration
+      platform->setBackendParameter("url", "http://localhost:PORT");
+      platform->setBackendParameter("api_key", "test_key");
+      
+      // Create a simple circuit
+      auto kernel = cudaq::make_kernel();
+      auto qubits = kernel.qalloc(2);
+      kernel.h(qubits[0]);
+      kernel.cx(qubits[0], qubits[1]);
+      kernel.mz(qubits);
+      
+      // Execute the circuit
+      auto counts = cudaq::sample(kernel);
+      
+      // Check results
+      EXPECT_EQ(counts.size(), 2);
+      EXPECT_TRUE(counts.has_key("00"));
+      EXPECT_TRUE(counts.has_key("11"));
+    }
+
+Mock Server
+----------
+
+Create a mock server for testing:
+
+.. code-block:: text
+
+    utils/mock_qpu/<provider_name>/
+    └── __init__.py
+
+Implement the mock server:
+
+.. code-block:: python
+
+    # __init__.py
+    from http.server import BaseHTTPRequestHandler, HTTPServer
+    import json
+    import sys
+    import time
+    
+    class ProviderNameMockServer(BaseHTTPRequestHandler):
+        def _set_headers(self, status_code=200):
+            self.send_response(status_code)
+            self.send_header('Content-type', 'application/json')
+            self.end_headers()
+    
+        def do_POST(self):
+            content_length = int(self.headers['Content-Length'])
+            post_data = self.rfile.read(content_length)
+            data = json.loads(post_data.decode('utf-8'))
+            
+            if self.path == '/jobs':
+                # Create a job
+                response = {
+                    'id': 'job-123',
+                    'status': 'QUEUED'
+                }
+                self._set_headers()
+                self.wfile.write(json.dumps(response).encode())
+            else:
+                self._set_headers(404)
+                self.wfile.write(json.dumps({'error': 'Not found'}).encode())
+    
+        def do_GET(self):
+            if self.path.startswith('/jobs/job-123'):
+                # Return job status and results
+                response = {
+                    'id': 'job-123',
+                    'status': 'COMPLETED',
+                    'results': {
+                        'counts': {
+                            '00': 500,
+                            '11': 500
+                        }
+                    }
+                }
+                self._set_headers()
+                self.wfile.write(json.dumps(response).encode())
+            else:
+                self._set_headers(404)
+                self.wfile.write(json.dumps({'error': 'Not found'}).encode())
+    
+    def startServer(port=8000):
+        server_address = ('', port)
+        httpd = HTTPServer(server_address, ProviderNameMockServer)
+        print(f'Starting mock server on port {port}...')
+        httpd.serve_forever()
+    
+    if __name__ == '__main__':
+        port = int(sys.argv[1]) if len(sys.argv) > 1 else 8000
+        startServer(port)
+
+Python Tests
+-----------
+
+Create Python tests for your backend:
+
+.. code-block:: python
+
+    # python/tests/backends/test_<provider_name>.py
+    import os
+    import sys
+    import time
+    import pytest
+    from multiprocessing import Process
+    
+    import cudaq
+    from cudaq import spin
+    
+    try:
+        from utils.mock_qpu.<provider_name> import startServer
+    except:
+        print("Mock qpu not available, skipping Provider Name 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():
+        # Set the targeted QPU
+        cudaq.set_target('<provider_name>',
+                        url=f'http://localhost:{port}',
+                        api_key="test_key")
+        
+        # Launch the Mock Server
+        p = Process(target=startServer, args=(port,))
+        p.start()
+        time.sleep(1)
+        
+        yield "Running the tests."
+        
+        # Kill the server
+        p.terminate()
+    
+    def test_<provider_name>_sample():
+        # Create the kernel
+        kernel = cudaq.make_kernel()
+        qubits = kernel.qalloc(2)
+        kernel.h(qubits[0])
+        kernel.cx(qubits[0], qubits[1])
+        kernel.mz(qubits)
+        
+        # Run sample
+        counts = cudaq.sample(kernel)
+        assert len(counts) == 2
+        assert '00' in counts
+        assert '11' in counts
+        
+        # Run sample asynchronously
+        future = cudaq.sample_async(kernel)
+        counts = future.get()
+        assert len(counts) == 2
+        assert '00' in counts
+        assert '11' in counts
+
+Documentation
+============
+
+Add documentation for your backend in the appropriate sections of the CUDA-Q documentation. This should include:
+
+1. How to configure and use the backend
+2. Any provider-specific parameters or features
+3. Examples of running circuits on the backend
+
+More specifically, you will need to modify at least the following files:
+* ``docs/sphinx/using/examples/hardware_providers.rst``
+* ``docs/sphinx/using/backends/hardware/<your-technology>.rst``
+* ``docs/sphinx/targets/python/<provider_name>.py``
+* ``docs/sphinx/targets/cpp/<provider_name>.cpp``
+
+Example Usage
+============
+
+Once your backend is implemented, users can use it as follows:
+
+.. code-block:: python
+
+    import cudaq
+    
+    # Set the target to your provider
+    cudaq.set_target('<provider_name>', 
+                    api_key='your_api_key',
+                    device='your_device')
+    
+    # Create and run a circuit
+    @cudaq.kernel
+    def bell():
+        qubits = cudaq.qvector(2)
+        h(qubits[0])
+        x.ctrl(qubits[0], qubits[1])
+        mz(qubits)
+    
+    # Run the circuit
+    counts = cudaq.sample(bell)
+    print(counts)
+
+Conclusion
+==========
+
+By following this guide, you can integrate a new quantum hardware provider with CUDA-Q. The integration involves creating a server helper, defining configuration files, and implementing tests. Once integrated, users can seamlessly run quantum circuits on your provider's hardware using the CUDA-Q framework.
path: docs/sphinx/using/backends/new_backends.rst
user: login: efratshabtai
body: Any instructions on how to create a PR with these changes?
created_at: 2025-05-12T16:25:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,528 @@
+*************************
+Adding New Backends
+*************************
+
+This guide explains how to integrate a new quantum hardware provider with CUDA-Q. The integration process involves creating a server helper that handles communication with the provider's API, defining configuration files, and implementing necessary tests.
+
+Overview
+========
+
+CUDA-Q supports various quantum hardware providers through a REST-based architecture. To add a new backend, you'll need to:
+
+1. Create a server helper class that handles API communication
+2. Define configuration files for the target
+3. Implement tests to verify functionality
+4. Add documentation for users
+
+The following sections detail each step of this process.
+
+Server Helper Implementation
+===========================
+
+The server helper is the core component that handles communication with the quantum hardware provider's API. It extends the ``ServerHelper`` base class and implements methods for job submission, result retrieval, and other provider-specific functionality.
+
+Directory Structure
+------------------
+
+Create the following directory structure for your new backend (replace ``<provider_name>`` with your provider's name):
+
+.. code-block:: text
+
+    runtime/cudaq/platform/default/rest/helpers/<provider_name>/
+    ├── CMakeLists.txt
+    ├── ProviderNameServerHelper.cpp
+    └── <provider_name>.yml
+
+Server Helper Class
+------------------
+
+Here's a template for implementing a server helper class:
+
+.. code-block:: cpp
+
+    // ProviderNameServerHelper.cpp
+    #include "common/Logger.h"
+    #include "common/RestClient.h"
+    #include "common/ServerHelper.h"
+    #include "cudaq/Support/Version.h"
+    #include "cudaq/utils/cudaq_utils.h"
+    #include <bitset>
+    #include <fstream>
+    #include <iostream>
+    #include <map>
+    #include <regex>
+    #include <sstream>
+    #include <thread>
+    #include <unordered_set>
+    
+    using json = nlohmann::json;
+    
+    namespace cudaq {
+    
+    /// @brief The ProviderNameServerHelper class extends the ServerHelper class
+    /// to handle interactions with the Provider Name server for submitting and
+    /// retrieving quantum computation jobs.
+    class ProviderNameServerHelper : public ServerHelper {
+      static constexpr const char *DEFAULT_URL = "https://api.provider-name.com";
+      static constexpr const char *DEFAULT_VERSION = "v1.0";
+    
+    public:
+      const std::string name() const override { return "<provider_name>"; }
+    
+      /// @brief Example implementation of authentication headers.
+      RestHeaders getHeaders() override {
+        RestHeaders headers;
+        headers["Content-Type"] = "application/json";
+        
+        // Add authentication headers if needed
+        if (backendConfig.count("api_key"))
+          headers["Authorization"] = "Bearer " + backendConfig["api_key"];
+        
+        return headers;
+      }
+    
+      /// @brief Example implementation of backend initialization.
+      void initialize(BackendConfig config) override {
+        cudaq::info("Initializing Provider Name Backend");
+        backendConfig = config;
+        
+        if (!backendConfig.count("url"))
+          backendConfig["url"] = DEFAULT_URL;
+        if (!backendConfig.count("version"))
+          backendConfig["version"] = DEFAULT_VERSION;
+
+        // Set shots if provided
+        if (config.find("shots") != config.end())
+          this->setShots(std::stoul(config["shots"]));
+      }
+    
+      /// @brief Example implementation of simple job creation.
+      ServerJobPayload createJob(std::vector<KernelExecution> &circuitCodes) override {
+        ServerMessage job;
+        job["content"] = circuitCodes[0].code;
+        job["shots"] = shots;
+        
+        RestHeaders headers = getHeaders();
+        std::string path = "/jobs";
+        
+        return std::make_tuple(backendConfig["url"] + path, headers, 
+                              std::vector<ServerMessage>{job});
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string extractJobId(ServerMessage &postResponse) override {
+        if (!postResponse.contains("id"))
+          return "";
+        
+        return postResponse.at("id");
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string constructGetJobPath(ServerMessage &postResponse) override {
+        return extractJobId(postResponse);
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string constructGetJobPath(std::string &jobId) override {
+        return backendConfig["url"] + "/jobs/" + jobId;
+      }
+    
+      /// @brief Example implementation of job status checking.
+      bool jobIsDone(ServerMessage &getJobResponse) override {
+        if (!getJobResponse.contains("status"))
+          return false;
+        
+        std::string status = getJobResponse["status"];
+        return status == "COMPLETED" || status == "FAILED";
+      }
+    
+      /// @brief Example implementation of result processing.
+      cudaq::sample_result processResults(ServerMessage &getJobResponse,
+                                         std::string &jobId) override {
+        cudaq::info("Processing results: {}", getJobResponse.dump());
+        
+        // Extract measurement results from the response
+        auto samplesJson = getJobResponse["results"]["counts"];
+        cudaq::CountsDictionary counts;
+        
+        for (auto &item : samplesJson.items()) {
+          std::string bitstring = item.key();
+          std::size_t count = item.value();
+          counts[bitstring] = count;
+        }
+        
+        // Create an ExecutionResult
+        cudaq::ExecutionResult execResult{counts};
+        
+        // Return the sample_result
+        return cudaq::sample_result{execResult};
+      }
+    
+      /// @brief Example implementation of polling configuration.
+      std::chrono::microseconds
+      nextResultPollingInterval(ServerMessage &postResponse) override {
+        return std::chrono::seconds(5);
+      }
+    };
+    
+    } // namespace cudaq
+    
+    // Register the server helper in the CUDA-Q server helper factory
+    CUDAQ_REGISTER_TYPE(cudaq::ServerHelper, cudaq::ProviderNameServerHelper, <provider_name>)
+
+``CMakeLists.txt``
+--------------
+
+Create a ``CMakeLists.txt`` file for your server helper:
+
+.. code-block:: cmake
+
+    # CMakeLists.txt
+    target_sources(cudaq-rest-qpu PRIVATE ProviderNameServerHelper.cpp)
+    add_target_config(<provider_name>)
+    
+    add_library(cudaq-serverhelper-<provider_name> SHARED ProviderNameServerHelper.cpp)
+    target_link_libraries(cudaq-serverhelper-<provider_name>
+      PUBLIC
+      cudaq-common
+      fmt::fmt-header-only
+    )
+    install(TARGETS cudaq-serverhelper-<provider_name> DESTINATION lib)
+
+Target Configuration
+===================
+
+Create a ``YAML`` configuration file for your target:
+
+.. code-block:: yaml
+
+    # <provider_name>.yml
+    name: "<provider_name>"
+    description: "CUDA-Q target for Provider Name."
+    
+    config:
+      # Tell DefaultQuantumPlatform what QPU subtype to use
+      platform-qpu: remote_rest
+      # Add the rest-qpu library to the link list
+      link-libs: ["-lcudaq-rest-qpu"]
+      # Tell NVQ++ to generate glue code to set the target backend name
+      gen-target-backend: true
+      # Add preprocessor defines to compilation
+      preprocessor-defines: ["-D CUDAQ_QUANTUM_DEVICE"]
+      # Define the lowering pipeline
+      # You will likely need assistance from the CUDA-Q team to set this up correctly
+      platform-lowering-config: "classical-optimization-pipeline,globalize-array-values,func.func(state-prep),unitary-synthesis,canonicalize,apply-op-specialization,aggressive-early-inlining,classical-optimization-pipeline,func.func(lower-to-cfg,canonicalize,multicontrol-decomposition),decomposition{enable-patterns=SToR1,TToR1,R1ToU3,U3ToRotations,CHToCX,CCZToCX,CRzToCX,CRyToCX,CRxToCX,CR1ToCX},quake-to-cc-prep,func.func(expand-control-veqs,combine-quantum-alloc,canonicalize,combine-measurements),symbol-dce"
+      # Tell the rest-qpu that we are generating OpenQASM 2.0.
+      # As of the time of this writing, qasm2, qir-base and qir-adaptive are supported.
+      codegen-emission: qasm2
+      # Library mode is only for simulators, physical backends must turn this off
+      library-mode: false
+    
+    # Some examples of target arguments are shown below.
+    # You do not need to add any arguments for your backend if you do not need them.
+    target-arguments:
+      - key: api-key
+        required: true
+        type: string
+        platform-arg: api_key
+        help-string: "API key for Provider Name."
+      - key: url
+        required: false
+        type: string
+        platform-arg: url
+        help-string: "Specify Provider Name API server URL."
+      - key: device
+        required: false
+        type: string
+        platform-arg: device
+        help-string: "Specify the Provider Name quantum device to use."
+
+Update Parent ``CMakeLists.txt``
+----------------------------
+
+Add your provider to the parent ``CMakeLists.txt`` file:
+
+.. code-block:: cmake
+
+    # runtime/cudaq/platform/default/rest/helpers/CMakeLists.txt
+    add_subdirectory(<provider_name>)
+    add_subdirectory(ionq)
+    add_subdirectory(iqm)
+    # ... other providers
+
+Testing
+=======
+
+Unit Tests
+----------
+
+Create unit tests for your server helper:
+
+1. Create a directory structure:
+
+.. code-block:: text
+
+    unittests/backends/<provider_name>/
+    ├── CMakeLists.txt
+    ├── ProviderNameStartServerAndTest.sh.in
+    └── ProviderNameTester.cpp
+
+2. Implement the test files:
+
+.. code-block:: cmake
+
+    # CMakeLists.txt
+    add_executable(ProviderNameTester ProviderNameTester.cpp)
+    target_link_libraries(ProviderNameTester
+      PRIVATE
+      cudaq-common
+      cudaq
+      gtest_main
+    )
+    
+    configure_file(${CMAKE_CURRENT_SOURCE_DIR}/ProviderNameStartServerAndTest.sh.in
+                  ${CMAKE_CURRENT_BINARY_DIR}/ProviderNameStartServerAndTest.sh @ONLY)
+    
+    add_test(NAME ProviderNameTester COMMAND ${CMAKE_CURRENT_BINARY_DIR}/ProviderNameStartServerAndTest.sh)
+    set_tests_properties(ProviderNameTester PROPERTIES TIMEOUT 120)
+
+3. Create a shell script to start the mock server and run tests:
+
+.. code-block:: bash
+
+    #!/bin/bash
+    
+    # Start the mock server
+    python3 -m utils.mock_qpu.<provider_name> @PORT@ &
+    SERVER_PID=$!
+    
+    # Wait for server to start
+    sleep 2
+    
+    # Run the test
+    @CMAKE_CURRENT_BINARY_DIR@/ProviderNameTester
+    TEST_STATUS=$?
+    
+    # Kill the server
+    kill $SERVER_PID
+    
+    # Return the test status
+    exit $TEST_STATUS
+
+4. Implement the C++ test:
+
+.. code-block:: cpp
+
+    // ProviderNameTester.cpp
+    #include "common/Logger.h"
+    #include "common/RestClient.h"
+    #include "common/ServerHelper.h"
+    #include "cudaq/platform/quantum_platform.h"
+    #include "gtest/gtest.h"
+    
+    TEST(ProviderNameTester, checkSimpleCircuit) {
+      // Initialize the platform
+      auto platform = cudaq::get_platform();
+      platform->setTargetBackend("<provider_name>");
+      
+      // Set configuration
+      platform->setBackendParameter("url", "http://localhost:PORT");
+      platform->setBackendParameter("api_key", "test_key");
+      
+      // Create a simple circuit
+      auto kernel = cudaq::make_kernel();
+      auto qubits = kernel.qalloc(2);
+      kernel.h(qubits[0]);
+      kernel.cx(qubits[0], qubits[1]);
+      kernel.mz(qubits);
+      
+      // Execute the circuit
+      auto counts = cudaq::sample(kernel);
+      
+      // Check results
+      EXPECT_EQ(counts.size(), 2);
+      EXPECT_TRUE(counts.has_key("00"));
+      EXPECT_TRUE(counts.has_key("11"));
+    }
+
+Mock Server
+----------
+
+Create a mock server for testing:
+
+.. code-block:: text
+
+    utils/mock_qpu/<provider_name>/
+    └── __init__.py
+
+Implement the mock server:
+
+.. code-block:: python
+
+    # __init__.py
+    from http.server import BaseHTTPRequestHandler, HTTPServer
+    import json
+    import sys
+    import time
+    
+    class ProviderNameMockServer(BaseHTTPRequestHandler):
+        def _set_headers(self, status_code=200):
+            self.send_response(status_code)
+            self.send_header('Content-type', 'application/json')
+            self.end_headers()
+    
+        def do_POST(self):
+            content_length = int(self.headers['Content-Length'])
+            post_data = self.rfile.read(content_length)
+            data = json.loads(post_data.decode('utf-8'))
+            
+            if self.path == '/jobs':
+                # Create a job
+                response = {
+                    'id': 'job-123',
+                    'status': 'QUEUED'
+                }
+                self._set_headers()
+                self.wfile.write(json.dumps(response).encode())
+            else:
+                self._set_headers(404)
+                self.wfile.write(json.dumps({'error': 'Not found'}).encode())
+    
+        def do_GET(self):
+            if self.path.startswith('/jobs/job-123'):
+                # Return job status and results
+                response = {
+                    'id': 'job-123',
+                    'status': 'COMPLETED',
+                    'results': {
+                        'counts': {
+                            '00': 500,
+                            '11': 500
+                        }
+                    }
+                }
+                self._set_headers()
+                self.wfile.write(json.dumps(response).encode())
+            else:
+                self._set_headers(404)
+                self.wfile.write(json.dumps({'error': 'Not found'}).encode())
+    
+    def startServer(port=8000):
+        server_address = ('', port)
+        httpd = HTTPServer(server_address, ProviderNameMockServer)
+        print(f'Starting mock server on port {port}...')
+        httpd.serve_forever()
+    
+    if __name__ == '__main__':
+        port = int(sys.argv[1]) if len(sys.argv) > 1 else 8000
+        startServer(port)
+
+Python Tests
+-----------
+
+Create Python tests for your backend:
+
+.. code-block:: python
+
+    # python/tests/backends/test_<provider_name>.py
+    import os
+    import sys
+    import time
+    import pytest
+    from multiprocessing import Process
+    
+    import cudaq
+    from cudaq import spin
+    
+    try:
+        from utils.mock_qpu.<provider_name> import startServer
+    except:
+        print("Mock qpu not available, skipping Provider Name 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():
+        # Set the targeted QPU
+        cudaq.set_target('<provider_name>',
+                        url=f'http://localhost:{port}',
+                        api_key="test_key")
+        
+        # Launch the Mock Server
+        p = Process(target=startServer, args=(port,))
+        p.start()
+        time.sleep(1)
+        
+        yield "Running the tests."
+        
+        # Kill the server
+        p.terminate()
+    
+    def test_<provider_name>_sample():
+        # Create the kernel
+        kernel = cudaq.make_kernel()
+        qubits = kernel.qalloc(2)
+        kernel.h(qubits[0])
+        kernel.cx(qubits[0], qubits[1])
+        kernel.mz(qubits)
+        
+        # Run sample
+        counts = cudaq.sample(kernel)
+        assert len(counts) == 2
+        assert '00' in counts
+        assert '11' in counts
+        
+        # Run sample asynchronously
+        future = cudaq.sample_async(kernel)
+        counts = future.get()
+        assert len(counts) == 2
+        assert '00' in counts
+        assert '11' in counts
+
+Documentation
+============
+
+Add documentation for your backend in the appropriate sections of the CUDA-Q documentation. This should include:
+
+1. How to configure and use the backend
+2. Any provider-specific parameters or features
+3. Examples of running circuits on the backend
+
path: docs/sphinx/using/backends/new_backends.rst
user: login: mawolf2023
body: @efratshabtai  I think that is it.  Just making sure to have an accurate logo and then put them alphabetical under each modality.  I can share the file to make the edits too. 
created_at: 2025-05-12T16:34:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,586 @@
+********************************************
+Extending CUDA-Q with a new Hardware Backend
+********************************************
+
+This guide explains how to integrate a new quantum hardware provider with CUDA-Q. The integration process involves creating a server helper that handles communication with the provider's API, defining configuration files, and implementing necessary tests.
+
+Overview
+========
+
+CUDA-Q supports various quantum hardware :doc:`backends <../backends/hardware>` through a REST-based architecture. To add a new backend, you'll need to:
+
+1. Create a server helper class that handles API communication
+2. Define configuration files for the target
+3. Implement tests to verify functionality
+4. Add documentation for users
+
+The following sections detail each step of this process.
+
+Server Helper Implementation
+============================
+
+The server helper is the core component that handles communication with the quantum hardware provider's API. It extends the ``ServerHelper`` base class and implements methods for job submission, result retrieval, and other provider-specific functionality. The base class definition can be found in the `CUDA-Q repository <https://github.com/NVIDIA/cuda-quantum/blob/main/runtime/common/ServerHelper.h>`_.
+
+Directory Structure
+-------------------
+
+Create the following directory structure for your new backend (replace ``<provider_name>`` with your provider's name):
+
+.. code-block:: text
+
+    runtime/cudaq/platform/default/rest/helpers/<provider_name>/
+    ├── CMakeLists.txt
+    ├── ProviderNameServerHelper.cpp
+    └── <provider_name>.yml
+
+Server Helper Class
+-------------------
+
+Here's a template for implementing a server helper class:
+
+.. code-block:: cpp
+
+    // ProviderNameServerHelper.cpp
+    #include "common/Logger.h"
+    #include "common/RestClient.h"
+    #include "common/ServerHelper.h"
+    #include "cudaq/Support/Version.h"
+    #include "cudaq/utils/cudaq_utils.h"
+    #include <bitset>
+    #include <fstream>
+    #include <iostream>
+    #include <map>
+    #include <regex>
+    #include <sstream>
+    #include <thread>
+    #include <unordered_set>
+    
+    using json = nlohmann::json;
+    
+    namespace cudaq {
+    
+    /// @brief The ProviderNameServerHelper class extends the ServerHelper class
+    /// to handle interactions with the Provider Name server for submitting and
+    /// retrieving quantum computation jobs.
+    class ProviderNameServerHelper : public ServerHelper {
+      static constexpr const char *DEFAULT_URL = "https://api.provider-name.com";
+      static constexpr const char *DEFAULT_VERSION = "v1.0";
+    
+    public:
+      const std::string name() const override { return "<provider_name>"; }
+    
+      /// @brief Example implementation of authentication headers.
+      RestHeaders getHeaders() override {
+        RestHeaders headers;
+        headers["Content-Type"] = "application/json";
+        
+        // Add authentication headers if needed
+        if (backendConfig.count("api_key"))
+          headers["Authorization"] = "Bearer " + backendConfig["api_key"];
+        
+        return headers;
+      }
+    
+      /// @brief Example implementation of backend initialization.
+      void initialize(BackendConfig config) override {
+        cudaq::info("Initializing Provider Name Backend");
+        backendConfig = config;
+        
+        if (!backendConfig.count("url"))
+          backendConfig["url"] = DEFAULT_URL;
+        if (!backendConfig.count("version"))
+          backendConfig["version"] = DEFAULT_VERSION;
+
+        // Set shots if provided
+        if (config.find("shots") != config.end())
+          this->setShots(std::stoul(config["shots"]));
+      }
+    
+      /// @brief Example implementation of simple job creation.
+      ServerJobPayload createJob(std::vector<KernelExecution> &circuitCodes) override {
+        ServerMessage job;
+        job["content"] = circuitCodes[0].code;
+        job["shots"] = shots;
+        
+        RestHeaders headers = getHeaders();
+        std::string path = "/jobs";
+        
+        return std::make_tuple(backendConfig["url"] + path, headers, 
+                              std::vector<ServerMessage>{job});
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string extractJobId(ServerMessage &postResponse) override {
+        if (!postResponse.contains("id"))
+          return "";
+        
+        return postResponse.at("id");
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string constructGetJobPath(ServerMessage &postResponse) override {
+        return extractJobId(postResponse);
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string constructGetJobPath(std::string &jobId) override {
+        return backendConfig["url"] + "/jobs/" + jobId;
+      }
+    
+      /// @brief Example implementation of job status checking.
+      bool jobIsDone(ServerMessage &getJobResponse) override {
+        if (!getJobResponse.contains("status"))
+          return false;
+        
+        std::string status = getJobResponse["status"];
+        return status == "COMPLETED" || status == "FAILED";
+      }
+    
+      /// @brief Example implementation of result processing.
+      ///
+      /// The raw results from quantum hardware often need post-processing (bit
+      /// reordering, normalization, etc.) to match CUDA-Q's expectations.
+      /// This is the place to do that.
+      cudaq::sample_result processResults(ServerMessage &getJobResponse,
+                                         std::string &jobId) override {
+        cudaq::info("Processing results: {}", getJobResponse.dump());
+        
+        // Extract measurement results from the response
+        auto samplesJson = getJobResponse["results"]["counts"];
+        cudaq::CountsDictionary counts;
+        
+        for (auto &item : samplesJson.items()) {
+          std::string bitstring = item.key();
+          std::size_t count = item.value();
+          counts[bitstring] = count;
+        }
+        
+        // Create an ExecutionResult
+        cudaq::ExecutionResult execResult{counts};
+        
+        // Return the sample_result
+        return cudaq::sample_result{execResult};
+      }
+    
+      /// @brief Example implementation of polling configuration.
+      std::chrono::microseconds
+      nextResultPollingInterval(ServerMessage &postResponse) override {
+        return std::chrono::seconds(5);
+      }
+    };
+    
+    } // namespace cudaq
+    
+    // Register the server helper in the CUDA-Q server helper factory
+    CUDAQ_REGISTER_TYPE(cudaq::ServerHelper, cudaq::ProviderNameServerHelper, <provider_name>)
+
+``CMakeLists.txt``
+------------------
+
+You will need to configure CUDA-Q's ``cmake`` system for your new server helper. By convention, you should setup your target as optional by adding a CMake flag in the ``CMakeLists.txt`` at the root of the CUDA-Q repository:
+
+.. code-block:: cmake
+
+    # Enable <provider_name> target by default
+    if (NOT DEFINED CUDAQ_ENABLE_PROVIDER_NAME_BACKEND)
+      set(CUDAQ_ENABLE_PROVIDER_NAME_BACKEND ON CACHE BOOL "Enable building the <Provider Name> target.")
+    endif()
+
+Then, create a ``CMakeLists.txt`` file in your server helper's directory and check for this flag:
+
+.. code-block:: cmake
+
+    if(CUDAQ_ENABLE_PROVIDER_NAME_BACKEND)
+      target_sources(cudaq-rest-qpu PRIVATE ProviderNameServerHelper.cpp)
+      add_target_config(<provider_name>)
+      
+      add_library(cudaq-serverhelper-<provider_name> SHARED ProviderNameServerHelper.cpp)
+      target_link_libraries(cudaq-serverhelper-<provider_name>
+        PUBLIC
+        cudaq-common
+        fmt::fmt-header-only
+      )
+      install(TARGETS cudaq-serverhelper-<provider_name> DESTINATION lib)
+    endif()
+
+Target Configuration
+====================
+
+Create a ``YAML`` configuration file for your target:
+
+.. code-block:: yaml
+
+    # <provider_name>.yml
+    name: "<provider_name>"
+    description: "CUDA-Q target for Provider Name."
+    
+    config:
+      # Tell DefaultQuantumPlatform what QPU subtype to use
+      platform-qpu: remote_rest
+      # Add the rest-qpu library to the link list
+      link-libs: ["-lcudaq-rest-qpu"]
+      # Tell NVQ++ to generate glue code to set the target backend name
+      gen-target-backend: true
+      # Add preprocessor defines to compilation
+      preprocessor-defines: ["-D CUDAQ_QUANTUM_DEVICE"]
+      # Define the lowering pipeline
+      # This will cover applying hardware-specific constraints since each provider may have different native gate sets, requiring custom mappings and decompositions. You may need assistance from the CUDA-Q team to set this up correctly.
+      platform-lowering-config: "classical-optimization-pipeline,globalize-array-values,func.func(state-prep),unitary-synthesis,canonicalize,apply-op-specialization,aggressive-early-inlining,classical-optimization-pipeline,func.func(lower-to-cfg,canonicalize,multicontrol-decomposition),decomposition{enable-patterns=SToR1,TToR1,R1ToU3,U3ToRotations,CHToCX,CCZToCX,CRzToCX,CRyToCX,CRxToCX,CR1ToCX},quake-to-cc-prep,func.func(expand-control-veqs,combine-quantum-alloc,canonicalize,combine-measurements),symbol-dce"
+      # Tell the rest-qpu that we are generating OpenQASM 2.0.
path: docs/sphinx/using/extending/backend.rst
user: login: khalatepradnya
body: Comment mismatch.
```suggestion
      # Tell the rest-qpu that we are generating QIR base profile.
```
created_at: 2025-05-16T18:52:34Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,622 @@
+********************************************
+Extending CUDA-Q with a new Hardware Backend
+********************************************
+
+This guide explains how to integrate a new quantum hardware provider with CUDA-Q. The integration process involves creating a server helper that handles communication with the provider's API, defining configuration files, and implementing necessary tests.
+
+Overview
+========
+
+CUDA-Q supports various quantum hardware :doc:`backends <../backends/hardware>` through a REST-based architecture. To add a new backend, you'll need to:
+
+1. Create a server helper class that handles API communication
+2. Define configuration files for the target
+3. Implement tests to verify functionality
+4. Add documentation for users
+
+The following sections detail each step of this process.
+
+Server Helper Implementation
+============================
+
+The server helper is the core component that handles communication with the quantum hardware provider's API. It extends the ``ServerHelper`` base class and implements methods for job submission, result retrieval, and other provider-specific functionality. The base class definition can be found in the `CUDA-Q repository <https://github.com/NVIDIA/cuda-quantum/blob/main/runtime/common/ServerHelper.h>`_.
+
+Directory Structure
+-------------------
+
+Create the following directory structure for your new backend (replace ``<provider_name>`` with your provider's name):
+
+.. code-block:: text
+
+    runtime/cudaq/platform/default/rest/helpers/<provider_name>/
+    ├── CMakeLists.txt
+    ├── ProviderNameServerHelper.cpp
+    └── <provider_name>.yml
+
+Server Helper Class
+-------------------
+
+Here's a template for implementing a server helper class:
+
+.. code-block:: cpp
+
+    // ProviderNameServerHelper.cpp
+    #include "common/Logger.h"
+    #include "common/RestClient.h"
+    #include "common/ServerHelper.h"
+    #include "cudaq/Support/Version.h"
+    #include "cudaq/utils/cudaq_utils.h"
+    #include <bitset>
+    #include <fstream>
+    #include <iostream>
+    #include <map>
+    #include <regex>
+    #include <sstream>
+    #include <thread>
+    #include <unordered_set>
+    
+    using json = nlohmann::json;
+    
+    namespace cudaq {
+    
+    /// @brief The ProviderNameServerHelper class extends the ServerHelper class
+    /// to handle interactions with the Provider Name server for submitting and
+    /// retrieving quantum computation jobs.
+    class ProviderNameServerHelper : public ServerHelper {
+      static constexpr const char *DEFAULT_URL = "https://api.provider-name.com";
+      static constexpr const char *DEFAULT_VERSION = "v1.0";
+    
+    public:
+      const std::string name() const override { return "<provider_name>"; }
+    
+      /// @brief Example implementation of authentication headers.
+      RestHeaders getHeaders() override {
+        RestHeaders headers;
+        headers["Content-Type"] = "application/json";
+        
+        // Add authentication headers if needed
+        if (backendConfig.count("api_key"))
+          headers["Authorization"] = "Bearer " + backendConfig["api_key"];
+        
+        return headers;
+      }
+    
+      /// @brief Example implementation of backend initialization.
+      void initialize(BackendConfig config) override {
+        cudaq::info("Initializing Provider Name Backend");
+        backendConfig = config;
+        
+        if (!backendConfig.count("url"))
+          backendConfig["url"] = DEFAULT_URL;
+        if (!backendConfig.count("version"))
+          backendConfig["version"] = DEFAULT_VERSION;
+
+        // Set shots if provided
+        if (config.find("shots") != config.end())
+          this->setShots(std::stoul(config["shots"]));
+      }
+    
+      /// @brief Example implementation of simple job creation.
+      ServerJobPayload createJob(std::vector<KernelExecution> &circuitCodes) override {
+        ServerMessage job;
+        job["content"] = circuitCodes[0].code;
+        job["shots"] = shots;
+        
+        RestHeaders headers = getHeaders();
+        std::string path = "/jobs";
+        
+        return std::make_tuple(backendConfig["url"] + path, headers, 
+                              std::vector<ServerMessage>{job});
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string extractJobId(ServerMessage &postResponse) override {
+        if (!postResponse.contains("id"))
+          return "";
+        
+        return postResponse.at("id");
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string constructGetJobPath(ServerMessage &postResponse) override {
+        return extractJobId(postResponse);
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string constructGetJobPath(std::string &jobId) override {
+        return backendConfig["url"] + "/jobs/" + jobId;
+      }
+    
+      /// @brief Example implementation of job status checking.
+      bool jobIsDone(ServerMessage &getJobResponse) override {
+        if (!getJobResponse.contains("status"))
+          return false;
+        
+        std::string status = getJobResponse["status"];
+        return status == "COMPLETED" || status == "FAILED";
+      }
+    
+      /// @brief Example implementation of result processing.
+      ///
+      /// The raw results from quantum hardware often need post-processing (bit
+      /// reordering, normalization, etc.) to match CUDA-Q's expectations.
+      /// This is the place to do that.
+      cudaq::sample_result processResults(ServerMessage &getJobResponse,
+                                         std::string &jobId) override {
+        cudaq::info("Processing results: {}", getJobResponse.dump());
+        
+        // Extract measurement results from the response
+        auto samplesJson = getJobResponse["results"]["counts"];
+        cudaq::CountsDictionary counts;
+        
+        for (auto &item : samplesJson.items()) {
+          std::string bitstring = item.key();
+          std::size_t count = item.value();
+          counts[bitstring] = count;
+        }
+        
+        // Create an ExecutionResult
+        cudaq::ExecutionResult execResult{counts};
+        
+        // Return the sample_result
+        return cudaq::sample_result{execResult};
+      }
+    
+      /// @brief Example implementation of polling configuration.
+      std::chrono::microseconds
+      nextResultPollingInterval(ServerMessage &postResponse) override {
+        return std::chrono::seconds(5);
+      }
+    };
+    
+    } // namespace cudaq
+    
+    // Register the server helper in the CUDA-Q server helper factory
+    CUDAQ_REGISTER_TYPE(cudaq::ServerHelper, cudaq::ProviderNameServerHelper, <provider_name>)
+
+``CMakeLists.txt``
+------------------
+
+You will need to configure CUDA-Q's ``cmake`` system for your new server helper. By convention, you should setup your target as optional by adding a CMake flag in the ``CMakeLists.txt`` at the root of the CUDA-Q repository:
+
+.. code-block:: cmake
+
+    # Enable <provider_name> target by default
+    if (NOT DEFINED CUDAQ_ENABLE_PROVIDER_NAME_BACKEND)
+      set(CUDAQ_ENABLE_PROVIDER_NAME_BACKEND ON CACHE BOOL "Enable building the <Provider Name> target.")
+    endif()
+
+Then, create a ``CMakeLists.txt`` file in your server helper's directory and check for this flag:
+
+.. code-block:: cmake
+
+    if(CUDAQ_ENABLE_PROVIDER_NAME_BACKEND)
+      target_sources(cudaq-rest-qpu PRIVATE ProviderNameServerHelper.cpp)
+      add_target_config(<provider_name>)
+      
+      add_library(cudaq-serverhelper-<provider_name> SHARED ProviderNameServerHelper.cpp)
+      target_link_libraries(cudaq-serverhelper-<provider_name>
+        PUBLIC
+        cudaq-common
+        fmt::fmt-header-only
+      )
+      install(TARGETS cudaq-serverhelper-<provider_name> DESTINATION lib)
+    endif()
+
+Target Configuration
+====================
+
+Create a ``YAML`` configuration file for your target:
+
+.. code-block:: yaml
+
+    # <provider_name>.yml
+    name: "<provider_name>"
+    description: "CUDA-Q target for Provider Name."
+    
+    config:
+      # Tell DefaultQuantumPlatform what QPU subtype to use
+      platform-qpu: remote_rest
+      # Add the rest-qpu library to the link list
+      link-libs: ["-lcudaq-rest-qpu"]
+      # Tell NVQ++ to generate glue code to set the target backend name
+      gen-target-backend: true
+      # Add preprocessor defines to compilation
+      preprocessor-defines: ["-D CUDAQ_QUANTUM_DEVICE"]
+      # Define the lowering pipeline
+      # This will cover applying hardware-specific constraints since each provider may have different native gate sets, requiring custom mappings and decompositions. You may need assistance from the CUDA-Q team to set this up correctly.
+      platform-lowering-config: "classical-optimization-pipeline,globalize-array-values,func.func(state-prep),unitary-synthesis,canonicalize,apply-op-specialization,aggressive-early-inlining,classical-optimization-pipeline,func.func(lower-to-cfg,canonicalize,multicontrol-decomposition),decomposition{enable-patterns=SToR1,TToR1,R1ToU3,U3ToRotations,CHToCX,CCZToCX,CRzToCX,CRyToCX,CRxToCX,CR1ToCX},quake-to-cc-prep,func.func(expand-control-veqs,combine-quantum-alloc,canonicalize,combine-measurements),symbol-dce"
+      # Tell the rest-qpu that we are generating QIR base profile.
+      # As of the time of this writing, qasm2, qir-base and qir-adaptive are supported.
+      codegen-emission: qir-base
+      # Library mode is only for simulators, physical backends must turn this off
+      library-mode: false
+    
+    # Some examples of target arguments are shown below.
+    # You do not need to add any arguments for your backend if you do not need them.
+    target-arguments:
+      - key: api-key
+        required: true
+        type: string
+        platform-arg: api_key
+        help-string: "API key for Provider Name."
+      - key: url
+        required: false
+        type: string
+        platform-arg: url
+        help-string: "Specify Provider Name API server URL."
+      - key: device
+        required: false
+        type: string
+        platform-arg: device
+        help-string: "Specify the Provider Name quantum device to use."
+
+Update Parent ``CMakeLists.txt``
+--------------------------------
+
+Add your provider to the parent ``CMakeLists.txt`` file:
+
+.. code-block:: cmake
+
+    # runtime/cudaq/platform/default/rest/helpers/CMakeLists.txt
+    add_subdirectory(<provider_name>)
+    add_subdirectory(ionq)
+    add_subdirectory(iqm)
+    # ... other providers
+
+Testing
+=======
+
+Unit Tests
+----------
+
+Create unit tests for your server helper:
+
+1. Create a directory structure:
+
+.. code-block:: text
+
+    unittests/backends/<provider_name>/
+    ├── CMakeLists.txt
+    ├── ProviderNameStartServerAndTest.sh.in
+    └── ProviderNameTester.cpp
+
+2. Implement the test files:
+
+.. code-block:: cmake
+
+    # CMakeLists.txt
+    add_executable(ProviderNameTester ProviderNameTester.cpp)
+    target_link_libraries(ProviderNameTester
+      PRIVATE
+      cudaq-common
+      cudaq
+      gtest_main
+    )
+    
+    configure_file(${CMAKE_CURRENT_SOURCE_DIR}/ProviderNameStartServerAndTest.sh.in
+                  ${CMAKE_CURRENT_BINARY_DIR}/ProviderNameStartServerAndTest.sh @ONLY)
+    
+    add_test(NAME ProviderNameTester COMMAND ${CMAKE_CURRENT_BINARY_DIR}/ProviderNameStartServerAndTest.sh)
+    set_tests_properties(ProviderNameTester PROPERTIES TIMEOUT 120)
+
+3. Create a shell script to start the mock server and run tests:
+
+.. code-block:: bash
+
+    #!/bin/bash
+    
+    # Start the mock server
+    python3 -m utils.mock_qpu.<provider_name> @PORT@ &
+    SERVER_PID=$!
+    
+    # Wait for server to start
+    sleep 2
+    
+    # Run the test
+    @CMAKE_CURRENT_BINARY_DIR@/ProviderNameTester
+    TEST_STATUS=$?
+    
+    # Kill the server
+    kill $SERVER_PID
+    
+    # Return the test status
+    exit $TEST_STATUS
+
+4. Implement the C++ test:
+
+.. code-block:: cpp
+
+    // ProviderNameTester.cpp
+    #include "common/Logger.h"
+    #include "common/RestClient.h"
+    #include "common/ServerHelper.h"
+    #include "cudaq/platform/quantum_platform.h"
+    #include "gtest/gtest.h"
+    
+    TEST(ProviderNameTester, checkSimpleCircuit) {
+      // Initialize the platform
+      auto platform = cudaq::get_platform();
+      platform->setTargetBackend("<provider_name>");
+      
+      // Set configuration
+      platform->setBackendParameter("url", "http://localhost:PORT");
+      platform->setBackendParameter("api_key", "test_key");
+      
+      // Create a simple circuit
+      auto kernel = cudaq::make_kernel();
+      auto qubits = kernel.qalloc(2);
+      kernel.h(qubits[0]);
+      kernel.cx(qubits[0], qubits[1]);
+      kernel.mz(qubits);
+      
+      // Execute the circuit
+      auto counts = cudaq::sample(kernel);
+      
+      // Check results
+      EXPECT_EQ(counts.size(), 2);
+      EXPECT_TRUE(counts.has_key("00"));
+      EXPECT_TRUE(counts.has_key("11"));
+    }
+
+To make sure the C++ tests don't run if your target is not enabled, add the following to ``targettests/lit.site.cfg.py.in``:
+
+.. code-block:: python
+
+    config.cudaq_backends_provider = "@CUDAQ_ENABLE_PROVIDER_NAME_BACKEND@"
+    if cmake_boolvar_to_bool(config.cudaq_backends_provider):
+        config.available_features.add('provider')
+        config.substitutions.append(('%provider_avail', 'true'))
+    else:
+        config.substitutions.append(('%provider_avail', 'false'))
+
+And add the following to your ``targettests`` ``.cpp`` file:
+
+.. code-block:: cpp
+
+    // RUN: if %provider_avail; then nvq++ %cpp_std --target provider %s -o %t.x; fi
+
+Mock Server
+-----------
+
+Create a mock server for testing:
+
+.. code-block:: text
+
+    utils/mock_qpu/<provider_name>/
+    └── __init__.py
+
+Implement the mock server:
+
+.. code-block:: python
+
+    # __init__.py
+    from http.server import BaseHTTPRequestHandler, HTTPServer
+    import json
+    import sys
+    import time
+    
+    class ProviderNameMockServer(BaseHTTPRequestHandler):
+        def _set_headers(self, status_code=200):
+            self.send_response(status_code)
+            self.send_header('Content-type', 'application/json')
+            self.end_headers()
+    
+        def do_POST(self):
+            content_length = int(self.headers['Content-Length'])
+            post_data = self.rfile.read(content_length)
+            data = json.loads(post_data.decode('utf-8'))
+            
+            if self.path == '/jobs':
+                # Create a job
+                response = {
+                    'id': 'job-123',
+                    'status': 'QUEUED'
+                }
+                self._set_headers()
+                self.wfile.write(json.dumps(response).encode())
+            else:
+                self._set_headers(404)
+                self.wfile.write(json.dumps({'error': 'Not found'}).encode())
+    
+        def do_GET(self):
+            if self.path.startswith('/jobs/job-123'):
+                # Return job status and results
+                response = {
+                    'id': 'job-123',
+                    'status': 'COMPLETED',
+                    'results': {
+                        'counts': {
+                            '00': 500,
+                            '11': 500
+                        }
+                    }
+                }
+                self._set_headers()
+                self.wfile.write(json.dumps(response).encode())
+            else:
+                self._set_headers(404)
+                self.wfile.write(json.dumps({'error': 'Not found'}).encode())
+    
+    def startServer(port=8000):
+        server_address = ('', port)
+        httpd = HTTPServer(server_address, ProviderNameMockServer)
+        print(f'Starting mock server on port {port}...')
+        httpd.serve_forever()
+    
+    if __name__ == '__main__':
+        port = int(sys.argv[1]) if len(sys.argv) > 1 else 8000
+        startServer(port)
+
+Python Tests
+------------
+
+Create Python tests for your backend:
+
+.. code-block:: python
+
+    # python/tests/backends/test_<provider_name>.py
+    import os
+    import sys
+    import time
+    import pytest
+    from multiprocessing import Process
+    
+    import cudaq
+    from cudaq import spin
+
+    skipIf<provider_name>NotInstalled = pytest.mark.skipif(
+        not (cudaq.has_target("<provider_name>")),
+        reason='Could not find `<provider_name>` in installation')
+    
+    try:
+        from utils.mock_qpu.<provider_name> import startServer
+    except:
+        print("Mock qpu not available, skipping Provider Name tests.")
+        pytest.skip("Mock qpu not available.", allow_module_level=True)
+    
+    # Define the port for the mock server - make sure this is unique
+    # across all tests.
+    port = 62444
+    
+    @pytest.fixture(scope="session", autouse=True)
+    def startUpMockServer():
+        # Set the targeted QPU
+        cudaq.set_target('<provider_name>',
+                        url=f'http://localhost:{port}',
+                        api_key="test_key")
+        
+        # Launch the Mock Server
+        p = Process(target=startServer, args=(port,))
+        p.start()
+        time.sleep(1)
+        
+        yield "Running the tests."
+        
+        # Kill the server
+        p.terminate()
+    
+    def test_<provider_name>_sample():
+        # Create the kernel
+        kernel = cudaq.make_kernel()
path: docs/sphinx/using/extending/backend.rst
user: login: khalatepradnya
body: Q: Curious why you chose builder mode here?
created_at: 2025-05-16T21:26:34Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,622 @@
+********************************************
+Extending CUDA-Q with a new Hardware Backend
+********************************************
+
+This guide explains how to integrate a new quantum hardware provider with CUDA-Q. The integration process involves creating a server helper that handles communication with the provider's API, defining configuration files, and implementing necessary tests.
+
+Overview
+========
+
+CUDA-Q supports various quantum hardware :doc:`backends <../backends/hardware>` through a REST-based architecture. To add a new backend, you'll need to:
+
+1. Create a server helper class that handles API communication
+2. Define configuration files for the target
+3. Implement tests to verify functionality
+4. Add documentation for users
+
+The following sections detail each step of this process.
+
+Server Helper Implementation
+============================
+
+The server helper is the core component that handles communication with the quantum hardware provider's API. It extends the ``ServerHelper`` base class and implements methods for job submission, result retrieval, and other provider-specific functionality. The base class definition can be found in the `CUDA-Q repository <https://github.com/NVIDIA/cuda-quantum/blob/main/runtime/common/ServerHelper.h>`_.
+
+Directory Structure
+-------------------
+
+Create the following directory structure for your new backend (replace ``<provider_name>`` with your provider's name):
+
+.. code-block:: text
+
+    runtime/cudaq/platform/default/rest/helpers/<provider_name>/
+    ├── CMakeLists.txt
+    ├── ProviderNameServerHelper.cpp
+    └── <provider_name>.yml
+
+Server Helper Class
+-------------------
+
+Here's a template for implementing a server helper class:
+
+.. code-block:: cpp
+
+    // ProviderNameServerHelper.cpp
+    #include "common/Logger.h"
+    #include "common/RestClient.h"
+    #include "common/ServerHelper.h"
+    #include "cudaq/Support/Version.h"
+    #include "cudaq/utils/cudaq_utils.h"
+    #include <bitset>
+    #include <fstream>
+    #include <iostream>
+    #include <map>
+    #include <regex>
+    #include <sstream>
+    #include <thread>
+    #include <unordered_set>
+    
+    using json = nlohmann::json;
+    
+    namespace cudaq {
+    
+    /// @brief The ProviderNameServerHelper class extends the ServerHelper class
+    /// to handle interactions with the Provider Name server for submitting and
+    /// retrieving quantum computation jobs.
+    class ProviderNameServerHelper : public ServerHelper {
+      static constexpr const char *DEFAULT_URL = "https://api.provider-name.com";
+      static constexpr const char *DEFAULT_VERSION = "v1.0";
+    
+    public:
+      const std::string name() const override { return "<provider_name>"; }
+    
+      /// @brief Example implementation of authentication headers.
+      RestHeaders getHeaders() override {
+        RestHeaders headers;
+        headers["Content-Type"] = "application/json";
+        
+        // Add authentication headers if needed
+        if (backendConfig.count("api_key"))
+          headers["Authorization"] = "Bearer " + backendConfig["api_key"];
+        
+        return headers;
+      }
+    
+      /// @brief Example implementation of backend initialization.
+      void initialize(BackendConfig config) override {
+        cudaq::info("Initializing Provider Name Backend");
+        backendConfig = config;
+        
+        if (!backendConfig.count("url"))
+          backendConfig["url"] = DEFAULT_URL;
+        if (!backendConfig.count("version"))
+          backendConfig["version"] = DEFAULT_VERSION;
+
+        // Set shots if provided
+        if (config.find("shots") != config.end())
+          this->setShots(std::stoul(config["shots"]));
+      }
+    
+      /// @brief Example implementation of simple job creation.
+      ServerJobPayload createJob(std::vector<KernelExecution> &circuitCodes) override {
+        ServerMessage job;
+        job["content"] = circuitCodes[0].code;
+        job["shots"] = shots;
+        
+        RestHeaders headers = getHeaders();
+        std::string path = "/jobs";
+        
+        return std::make_tuple(backendConfig["url"] + path, headers, 
+                              std::vector<ServerMessage>{job});
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string extractJobId(ServerMessage &postResponse) override {
+        if (!postResponse.contains("id"))
+          return "";
+        
+        return postResponse.at("id");
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string constructGetJobPath(ServerMessage &postResponse) override {
+        return extractJobId(postResponse);
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string constructGetJobPath(std::string &jobId) override {
+        return backendConfig["url"] + "/jobs/" + jobId;
+      }
+    
+      /// @brief Example implementation of job status checking.
+      bool jobIsDone(ServerMessage &getJobResponse) override {
+        if (!getJobResponse.contains("status"))
+          return false;
+        
+        std::string status = getJobResponse["status"];
+        return status == "COMPLETED" || status == "FAILED";
+      }
+    
+      /// @brief Example implementation of result processing.
+      ///
+      /// The raw results from quantum hardware often need post-processing (bit
+      /// reordering, normalization, etc.) to match CUDA-Q's expectations.
+      /// This is the place to do that.
+      cudaq::sample_result processResults(ServerMessage &getJobResponse,
+                                         std::string &jobId) override {
+        cudaq::info("Processing results: {}", getJobResponse.dump());
+        
+        // Extract measurement results from the response
+        auto samplesJson = getJobResponse["results"]["counts"];
+        cudaq::CountsDictionary counts;
+        
+        for (auto &item : samplesJson.items()) {
+          std::string bitstring = item.key();
+          std::size_t count = item.value();
+          counts[bitstring] = count;
+        }
+        
+        // Create an ExecutionResult
+        cudaq::ExecutionResult execResult{counts};
+        
+        // Return the sample_result
+        return cudaq::sample_result{execResult};
+      }
+    
+      /// @brief Example implementation of polling configuration.
+      std::chrono::microseconds
+      nextResultPollingInterval(ServerMessage &postResponse) override {
+        return std::chrono::seconds(5);
+      }
+    };
+    
+    } // namespace cudaq
+    
+    // Register the server helper in the CUDA-Q server helper factory
+    CUDAQ_REGISTER_TYPE(cudaq::ServerHelper, cudaq::ProviderNameServerHelper, <provider_name>)
+
+``CMakeLists.txt``
+------------------
+
+You will need to configure CUDA-Q's ``cmake`` system for your new server helper. By convention, you should setup your target as optional by adding a CMake flag in the ``CMakeLists.txt`` at the root of the CUDA-Q repository:
+
+.. code-block:: cmake
+
+    # Enable <provider_name> target by default
+    if (NOT DEFINED CUDAQ_ENABLE_PROVIDER_NAME_BACKEND)
+      set(CUDAQ_ENABLE_PROVIDER_NAME_BACKEND ON CACHE BOOL "Enable building the <Provider Name> target.")
+    endif()
+
+Then, create a ``CMakeLists.txt`` file in your server helper's directory and check for this flag:
+
+.. code-block:: cmake
+
+    if(CUDAQ_ENABLE_PROVIDER_NAME_BACKEND)
+      target_sources(cudaq-rest-qpu PRIVATE ProviderNameServerHelper.cpp)
+      add_target_config(<provider_name>)
+      
+      add_library(cudaq-serverhelper-<provider_name> SHARED ProviderNameServerHelper.cpp)
+      target_link_libraries(cudaq-serverhelper-<provider_name>
+        PUBLIC
+        cudaq-common
+        fmt::fmt-header-only
+      )
+      install(TARGETS cudaq-serverhelper-<provider_name> DESTINATION lib)
+    endif()
+
+Target Configuration
+====================
+
+Create a ``YAML`` configuration file for your target:
+
+.. code-block:: yaml
+
+    # <provider_name>.yml
+    name: "<provider_name>"
+    description: "CUDA-Q target for Provider Name."
+    
+    config:
+      # Tell DefaultQuantumPlatform what QPU subtype to use
+      platform-qpu: remote_rest
+      # Add the rest-qpu library to the link list
+      link-libs: ["-lcudaq-rest-qpu"]
+      # Tell NVQ++ to generate glue code to set the target backend name
+      gen-target-backend: true
+      # Add preprocessor defines to compilation
+      preprocessor-defines: ["-D CUDAQ_QUANTUM_DEVICE"]
+      # Define the lowering pipeline
+      # This will cover applying hardware-specific constraints since each provider may have different native gate sets, requiring custom mappings and decompositions. You may need assistance from the CUDA-Q team to set this up correctly.
+      platform-lowering-config: "classical-optimization-pipeline,globalize-array-values,func.func(state-prep),unitary-synthesis,canonicalize,apply-op-specialization,aggressive-early-inlining,classical-optimization-pipeline,func.func(lower-to-cfg,canonicalize,multicontrol-decomposition),decomposition{enable-patterns=SToR1,TToR1,R1ToU3,U3ToRotations,CHToCX,CCZToCX,CRzToCX,CRyToCX,CRxToCX,CR1ToCX},quake-to-cc-prep,func.func(expand-control-veqs,combine-quantum-alloc,canonicalize,combine-measurements),symbol-dce"
+      # Tell the rest-qpu that we are generating QIR base profile.
+      # As of the time of this writing, qasm2, qir-base and qir-adaptive are supported.
+      codegen-emission: qir-base
+      # Library mode is only for simulators, physical backends must turn this off
+      library-mode: false
+    
+    # Some examples of target arguments are shown below.
+    # You do not need to add any arguments for your backend if you do not need them.
+    target-arguments:
+      - key: api-key
+        required: true
+        type: string
+        platform-arg: api_key
+        help-string: "API key for Provider Name."
+      - key: url
+        required: false
+        type: string
+        platform-arg: url
+        help-string: "Specify Provider Name API server URL."
+      - key: device
+        required: false
+        type: string
+        platform-arg: device
+        help-string: "Specify the Provider Name quantum device to use."
+
+Update Parent ``CMakeLists.txt``
+--------------------------------
+
+Add your provider to the parent ``CMakeLists.txt`` file:
+
+.. code-block:: cmake
+
+    # runtime/cudaq/platform/default/rest/helpers/CMakeLists.txt
+    add_subdirectory(<provider_name>)
+    add_subdirectory(ionq)
+    add_subdirectory(iqm)
+    # ... other providers
+
+Testing
+=======
+
+Unit Tests
+----------
+
+Create unit tests for your server helper:
+
+1. Create a directory structure:
+
+.. code-block:: text
+
+    unittests/backends/<provider_name>/
+    ├── CMakeLists.txt
+    ├── ProviderNameStartServerAndTest.sh.in
+    └── ProviderNameTester.cpp
+
+2. Implement the test files:
+
+.. code-block:: cmake
+
+    # CMakeLists.txt
+    add_executable(ProviderNameTester ProviderNameTester.cpp)
+    target_link_libraries(ProviderNameTester
+      PRIVATE
+      cudaq-common
+      cudaq
+      gtest_main
+    )
+    
+    configure_file(${CMAKE_CURRENT_SOURCE_DIR}/ProviderNameStartServerAndTest.sh.in
+                  ${CMAKE_CURRENT_BINARY_DIR}/ProviderNameStartServerAndTest.sh @ONLY)
+    
+    add_test(NAME ProviderNameTester COMMAND ${CMAKE_CURRENT_BINARY_DIR}/ProviderNameStartServerAndTest.sh)
+    set_tests_properties(ProviderNameTester PROPERTIES TIMEOUT 120)
+
+3. Create a shell script to start the mock server and run tests:
+
+.. code-block:: bash
+
+    #!/bin/bash
+    
+    # Start the mock server
+    python3 -m utils.mock_qpu.<provider_name> @PORT@ &
+    SERVER_PID=$!
+    
+    # Wait for server to start
+    sleep 2
+    
+    # Run the test
+    @CMAKE_CURRENT_BINARY_DIR@/ProviderNameTester
+    TEST_STATUS=$?
+    
+    # Kill the server
+    kill $SERVER_PID
+    
+    # Return the test status
+    exit $TEST_STATUS
+
+4. Implement the C++ test:
+
+.. code-block:: cpp
+
+    // ProviderNameTester.cpp
+    #include "common/Logger.h"
+    #include "common/RestClient.h"
+    #include "common/ServerHelper.h"
+    #include "cudaq/platform/quantum_platform.h"
+    #include "gtest/gtest.h"
+    
+    TEST(ProviderNameTester, checkSimpleCircuit) {
+      // Initialize the platform
+      auto platform = cudaq::get_platform();
+      platform->setTargetBackend("<provider_name>");
+      
+      // Set configuration
+      platform->setBackendParameter("url", "http://localhost:PORT");
+      platform->setBackendParameter("api_key", "test_key");
+      
+      // Create a simple circuit
+      auto kernel = cudaq::make_kernel();
+      auto qubits = kernel.qalloc(2);
+      kernel.h(qubits[0]);
+      kernel.cx(qubits[0], qubits[1]);
+      kernel.mz(qubits);
+      
+      // Execute the circuit
+      auto counts = cudaq::sample(kernel);
+      
+      // Check results
+      EXPECT_EQ(counts.size(), 2);
+      EXPECT_TRUE(counts.has_key("00"));
+      EXPECT_TRUE(counts.has_key("11"));
+    }
+
+To make sure the C++ tests don't run if your target is not enabled, add the following to ``targettests/lit.site.cfg.py.in``:
+
+.. code-block:: python
+
+    config.cudaq_backends_provider = "@CUDAQ_ENABLE_PROVIDER_NAME_BACKEND@"
+    if cmake_boolvar_to_bool(config.cudaq_backends_provider):
+        config.available_features.add('provider')
+        config.substitutions.append(('%provider_avail', 'true'))
+    else:
+        config.substitutions.append(('%provider_avail', 'false'))
+
+And add the following to your ``targettests`` ``.cpp`` file:
+
+.. code-block:: cpp
+
+    // RUN: if %provider_avail; then nvq++ %cpp_std --target provider %s -o %t.x; fi
+
+Mock Server
+-----------
+
+Create a mock server for testing:
+
+.. code-block:: text
+
+    utils/mock_qpu/<provider_name>/
+    └── __init__.py
+
+Implement the mock server:
+
+.. code-block:: python
+
+    # __init__.py
+    from http.server import BaseHTTPRequestHandler, HTTPServer
+    import json
+    import sys
+    import time
+    
+    class ProviderNameMockServer(BaseHTTPRequestHandler):
+        def _set_headers(self, status_code=200):
+            self.send_response(status_code)
+            self.send_header('Content-type', 'application/json')
+            self.end_headers()
+    
+        def do_POST(self):
+            content_length = int(self.headers['Content-Length'])
+            post_data = self.rfile.read(content_length)
+            data = json.loads(post_data.decode('utf-8'))
+            
+            if self.path == '/jobs':
+                # Create a job
+                response = {
+                    'id': 'job-123',
+                    'status': 'QUEUED'
+                }
+                self._set_headers()
+                self.wfile.write(json.dumps(response).encode())
+            else:
+                self._set_headers(404)
+                self.wfile.write(json.dumps({'error': 'Not found'}).encode())
+    
+        def do_GET(self):
+            if self.path.startswith('/jobs/job-123'):
+                # Return job status and results
+                response = {
+                    'id': 'job-123',
+                    'status': 'COMPLETED',
+                    'results': {
+                        'counts': {
+                            '00': 500,
+                            '11': 500
+                        }
+                    }
+                }
+                self._set_headers()
+                self.wfile.write(json.dumps(response).encode())
+            else:
+                self._set_headers(404)
+                self.wfile.write(json.dumps({'error': 'Not found'}).encode())
+    
+    def startServer(port=8000):
+        server_address = ('', port)
+        httpd = HTTPServer(server_address, ProviderNameMockServer)
+        print(f'Starting mock server on port {port}...')
+        httpd.serve_forever()
+    
+    if __name__ == '__main__':
+        port = int(sys.argv[1]) if len(sys.argv) > 1 else 8000
+        startServer(port)
+
+Python Tests
+------------
+
+Create Python tests for your backend:
+
+.. code-block:: python
+
+    # python/tests/backends/test_<provider_name>.py
+    import os
+    import sys
+    import time
+    import pytest
+    from multiprocessing import Process
+    
+    import cudaq
+    from cudaq import spin
+
+    skipIf<provider_name>NotInstalled = pytest.mark.skipif(
+        not (cudaq.has_target("<provider_name>")),
+        reason='Could not find `<provider_name>` in installation')
+    
+    try:
+        from utils.mock_qpu.<provider_name> import startServer
+    except:
+        print("Mock qpu not available, skipping Provider Name tests.")
+        pytest.skip("Mock qpu not available.", allow_module_level=True)
+    
+    # Define the port for the mock server - make sure this is unique
+    # across all tests.
+    port = 62444
+    
+    @pytest.fixture(scope="session", autouse=True)
+    def startUpMockServer():
+        # Set the targeted QPU
+        cudaq.set_target('<provider_name>',
+                        url=f'http://localhost:{port}',
+                        api_key="test_key")
+        
+        # Launch the Mock Server
+        p = Process(target=startServer, args=(port,))
+        p.start()
+        time.sleep(1)
+        
+        yield "Running the tests."
+        
+        # Kill the server
+        p.terminate()
+    
+    def test_<provider_name>_sample():
+        # Create the kernel
+        kernel = cudaq.make_kernel()
+        qubits = kernel.qalloc(2)
+        kernel.h(qubits[0])
+        kernel.cx(qubits[0], qubits[1])
+        kernel.mz(qubits)
+        
+        # Run sample
+        counts = cudaq.sample(kernel)
+        assert len(counts) == 2
+        assert '00' in counts
+        assert '11' in counts
+        
+        # Run sample asynchronously
+        future = cudaq.sample_async(kernel)
+        counts = future.get()
+        assert len(counts) == 2
+        assert '00' in counts
+        assert '11' in counts
+
+Integration Tests
+-----------------
+
+To ensure proper execution on the hardware, a validation backend must be provided that:
+
+1. Consumes the same format that your target will use
+2. Validates that circuits passing this validation will execute successfully on the actual hardware
+3. Can be accessed by CUDA-Q's GitHub CI/CD pipelines
+
+Your validation backend doesn't need to be publicly available, but it should:
+
+- Accept the same input format as your actual quantum processor
+- Return meaningful error messages for invalid circuits
+- Provide an API endpoint that can be called from our integration tests
+
+If your validation backend is not publicly available, please coordinate the exchange of necessary credentials for CI/CD with the CUDA-Q team.
+
+Add your target to ``.github/workflows/integration_tests.yml``:
+
+.. code-block:: yaml
+
+    - name: Submit to <provider_name> test server
+      if: (success() || failure()) && (inputs.target == '<provider_name>' || github.event_name == 'schedule')
+      run: |
+        echo "### Submit to <provider_name> server" >> $GITHUB_STEP_SUMMARY
+        # Set up any required dependencies        
+        # Set up environment variables for authentication
+        export PROVIDER_API_KEY='${{ secrets.PROVIDER_API_KEY }}'
+        
+        # Run the integration tests
+        python_tests="docs/sphinx/targets/python/<provider_name>.py"
+        cpp_tests="docs/sphinx/targets/cpp/<provider_name>.cpp"
+        
+        # Execute tests (see other provider examples for implementation details)
+        # ...
+
+Documentation
+=============
+
+Add documentation for your backend in the appropriate sections of the CUDA-Q documentation. This should include:
+
+1. How to access your server (authentication set up, documentation etc.)
+2. How to configure and use the backend
+3. Any provider-specific parameters or features
+4. Examples of running circuits on the backend
+5. Adding your logo to the diagram on :doc:`../backends/hardware`
+
+More specifically, you will need to modify at least the following files:
+* ``docs/sphinx/using/examples/hardware_providers.rst``
+* ``docs/sphinx/using/backends/hardware.rst``
+* ``docs/sphinx/using/backends/hardware/<your-technology>.rst``
+* ``docs/sphinx/targets/python/<provider_name>.py``
+* ``docs/sphinx/targets/cpp/<provider_name>.cpp``
path: docs/sphinx/using/extending/backend.rst
user: login: khalatepradnya
body: These are not rendered as a list (unless that was the intention).
<img width="734" alt="image" src="https://github.com/user-attachments/assets/ee8b84eb-f88f-4b8f-8dff-a6a4fd4961d5" />

created_at: 2025-05-16T21:28:30Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,622 @@
+********************************************
+Extending CUDA-Q with a new Hardware Backend
+********************************************
+
+This guide explains how to integrate a new quantum hardware provider with CUDA-Q. The integration process involves creating a server helper that handles communication with the provider's API, defining configuration files, and implementing necessary tests.
+
+Overview
+========
+
+CUDA-Q supports various quantum hardware :doc:`backends <../backends/hardware>` through a REST-based architecture. To add a new backend, you'll need to:
+
+1. Create a server helper class that handles API communication
+2. Define configuration files for the target
+3. Implement tests to verify functionality
+4. Add documentation for users
+
+The following sections detail each step of this process.
+
+Server Helper Implementation
+============================
+
+The server helper is the core component that handles communication with the quantum hardware provider's API. It extends the ``ServerHelper`` base class and implements methods for job submission, result retrieval, and other provider-specific functionality. The base class definition can be found in the `CUDA-Q repository <https://github.com/NVIDIA/cuda-quantum/blob/main/runtime/common/ServerHelper.h>`_.
+
+Directory Structure
+-------------------
+
+Create the following directory structure for your new backend (replace ``<provider_name>`` with your provider's name):
+
+.. code-block:: text
+
+    runtime/cudaq/platform/default/rest/helpers/<provider_name>/
+    ├── CMakeLists.txt
+    ├── ProviderNameServerHelper.cpp
+    └── <provider_name>.yml
+
+Server Helper Class
+-------------------
+
+Here's a template for implementing a server helper class:
+
+.. code-block:: cpp
+
+    // ProviderNameServerHelper.cpp
+    #include "common/Logger.h"
+    #include "common/RestClient.h"
+    #include "common/ServerHelper.h"
+    #include "cudaq/Support/Version.h"
+    #include "cudaq/utils/cudaq_utils.h"
+    #include <bitset>
+    #include <fstream>
+    #include <iostream>
+    #include <map>
+    #include <regex>
+    #include <sstream>
+    #include <thread>
+    #include <unordered_set>
+    
+    using json = nlohmann::json;
+    
+    namespace cudaq {
+    
+    /// @brief The ProviderNameServerHelper class extends the ServerHelper class
+    /// to handle interactions with the Provider Name server for submitting and
+    /// retrieving quantum computation jobs.
+    class ProviderNameServerHelper : public ServerHelper {
+      static constexpr const char *DEFAULT_URL = "https://api.provider-name.com";
+      static constexpr const char *DEFAULT_VERSION = "v1.0";
+    
+    public:
+      const std::string name() const override { return "<provider_name>"; }
+    
+      /// @brief Example implementation of authentication headers.
+      RestHeaders getHeaders() override {
+        RestHeaders headers;
+        headers["Content-Type"] = "application/json";
+        
+        // Add authentication headers if needed
+        if (backendConfig.count("api_key"))
+          headers["Authorization"] = "Bearer " + backendConfig["api_key"];
+        
+        return headers;
+      }
+    
+      /// @brief Example implementation of backend initialization.
+      void initialize(BackendConfig config) override {
+        cudaq::info("Initializing Provider Name Backend");
+        backendConfig = config;
+        
+        if (!backendConfig.count("url"))
+          backendConfig["url"] = DEFAULT_URL;
+        if (!backendConfig.count("version"))
+          backendConfig["version"] = DEFAULT_VERSION;
+
+        // Set shots if provided
+        if (config.find("shots") != config.end())
+          this->setShots(std::stoul(config["shots"]));
+      }
+    
+      /// @brief Example implementation of simple job creation.
+      ServerJobPayload createJob(std::vector<KernelExecution> &circuitCodes) override {
+        ServerMessage job;
+        job["content"] = circuitCodes[0].code;
+        job["shots"] = shots;
+        
+        RestHeaders headers = getHeaders();
+        std::string path = "/jobs";
+        
+        return std::make_tuple(backendConfig["url"] + path, headers, 
+                              std::vector<ServerMessage>{job});
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string extractJobId(ServerMessage &postResponse) override {
+        if (!postResponse.contains("id"))
+          return "";
+        
+        return postResponse.at("id");
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string constructGetJobPath(ServerMessage &postResponse) override {
+        return extractJobId(postResponse);
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string constructGetJobPath(std::string &jobId) override {
+        return backendConfig["url"] + "/jobs/" + jobId;
+      }
+    
+      /// @brief Example implementation of job status checking.
+      bool jobIsDone(ServerMessage &getJobResponse) override {
+        if (!getJobResponse.contains("status"))
+          return false;
+        
+        std::string status = getJobResponse["status"];
+        return status == "COMPLETED" || status == "FAILED";
+      }
+    
+      /// @brief Example implementation of result processing.
+      ///
+      /// The raw results from quantum hardware often need post-processing (bit
+      /// reordering, normalization, etc.) to match CUDA-Q's expectations.
+      /// This is the place to do that.
+      cudaq::sample_result processResults(ServerMessage &getJobResponse,
+                                         std::string &jobId) override {
+        cudaq::info("Processing results: {}", getJobResponse.dump());
+        
+        // Extract measurement results from the response
+        auto samplesJson = getJobResponse["results"]["counts"];
+        cudaq::CountsDictionary counts;
+        
+        for (auto &item : samplesJson.items()) {
+          std::string bitstring = item.key();
+          std::size_t count = item.value();
+          counts[bitstring] = count;
+        }
+        
+        // Create an ExecutionResult
+        cudaq::ExecutionResult execResult{counts};
+        
+        // Return the sample_result
+        return cudaq::sample_result{execResult};
+      }
+    
+      /// @brief Example implementation of polling configuration.
+      std::chrono::microseconds
+      nextResultPollingInterval(ServerMessage &postResponse) override {
+        return std::chrono::seconds(5);
+      }
+    };
+    
+    } // namespace cudaq
+    
+    // Register the server helper in the CUDA-Q server helper factory
+    CUDAQ_REGISTER_TYPE(cudaq::ServerHelper, cudaq::ProviderNameServerHelper, <provider_name>)
+
+``CMakeLists.txt``
+------------------
+
+You will need to configure CUDA-Q's ``cmake`` system for your new server helper. By convention, you should setup your target as optional by adding a CMake flag in the ``CMakeLists.txt`` at the root of the CUDA-Q repository:
+
+.. code-block:: cmake
+
+    # Enable <provider_name> target by default
+    if (NOT DEFINED CUDAQ_ENABLE_PROVIDER_NAME_BACKEND)
+      set(CUDAQ_ENABLE_PROVIDER_NAME_BACKEND ON CACHE BOOL "Enable building the <Provider Name> target.")
+    endif()
+
+Then, create a ``CMakeLists.txt`` file in your server helper's directory and check for this flag:
+
+.. code-block:: cmake
+
+    if(CUDAQ_ENABLE_PROVIDER_NAME_BACKEND)
+      target_sources(cudaq-rest-qpu PRIVATE ProviderNameServerHelper.cpp)
+      add_target_config(<provider_name>)
+      
+      add_library(cudaq-serverhelper-<provider_name> SHARED ProviderNameServerHelper.cpp)
+      target_link_libraries(cudaq-serverhelper-<provider_name>
+        PUBLIC
+        cudaq-common
+        fmt::fmt-header-only
+      )
+      install(TARGETS cudaq-serverhelper-<provider_name> DESTINATION lib)
+    endif()
+
+Target Configuration
+====================
+
+Create a ``YAML`` configuration file for your target:
+
+.. code-block:: yaml
+
+    # <provider_name>.yml
+    name: "<provider_name>"
+    description: "CUDA-Q target for Provider Name."
+    
+    config:
+      # Tell DefaultQuantumPlatform what QPU subtype to use
+      platform-qpu: remote_rest
+      # Add the rest-qpu library to the link list
+      link-libs: ["-lcudaq-rest-qpu"]
+      # Tell NVQ++ to generate glue code to set the target backend name
+      gen-target-backend: true
+      # Add preprocessor defines to compilation
+      preprocessor-defines: ["-D CUDAQ_QUANTUM_DEVICE"]
+      # Define the lowering pipeline
+      # This will cover applying hardware-specific constraints since each provider may have different native gate sets, requiring custom mappings and decompositions. You may need assistance from the CUDA-Q team to set this up correctly.
+      platform-lowering-config: "classical-optimization-pipeline,globalize-array-values,func.func(state-prep),unitary-synthesis,canonicalize,apply-op-specialization,aggressive-early-inlining,classical-optimization-pipeline,func.func(lower-to-cfg,canonicalize,multicontrol-decomposition),decomposition{enable-patterns=SToR1,TToR1,R1ToU3,U3ToRotations,CHToCX,CCZToCX,CRzToCX,CRyToCX,CRxToCX,CR1ToCX},quake-to-cc-prep,func.func(expand-control-veqs,combine-quantum-alloc,canonicalize,combine-measurements),symbol-dce"
+      # Tell the rest-qpu that we are generating QIR base profile.
+      # As of the time of this writing, qasm2, qir-base and qir-adaptive are supported.
+      codegen-emission: qir-base
+      # Library mode is only for simulators, physical backends must turn this off
+      library-mode: false
+    
+    # Some examples of target arguments are shown below.
+    # You do not need to add any arguments for your backend if you do not need them.
+    target-arguments:
+      - key: api-key
+        required: true
+        type: string
+        platform-arg: api_key
+        help-string: "API key for Provider Name."
+      - key: url
+        required: false
+        type: string
+        platform-arg: url
+        help-string: "Specify Provider Name API server URL."
+      - key: device
+        required: false
+        type: string
+        platform-arg: device
+        help-string: "Specify the Provider Name quantum device to use."
+
+Update Parent ``CMakeLists.txt``
+--------------------------------
+
+Add your provider to the parent ``CMakeLists.txt`` file:
+
+.. code-block:: cmake
+
+    # runtime/cudaq/platform/default/rest/helpers/CMakeLists.txt
+    add_subdirectory(<provider_name>)
+    add_subdirectory(ionq)
+    add_subdirectory(iqm)
+    # ... other providers
+
+Testing
+=======
+
+Unit Tests
+----------
+
+Create unit tests for your server helper:
+
+1. Create a directory structure:
+
+.. code-block:: text
+
+    unittests/backends/<provider_name>/
+    ├── CMakeLists.txt
+    ├── ProviderNameStartServerAndTest.sh.in
+    └── ProviderNameTester.cpp
+
+2. Implement the test files:
+
+.. code-block:: cmake
+
+    # CMakeLists.txt
+    add_executable(ProviderNameTester ProviderNameTester.cpp)
+    target_link_libraries(ProviderNameTester
+      PRIVATE
+      cudaq-common
+      cudaq
+      gtest_main
+    )
+    
+    configure_file(${CMAKE_CURRENT_SOURCE_DIR}/ProviderNameStartServerAndTest.sh.in
+                  ${CMAKE_CURRENT_BINARY_DIR}/ProviderNameStartServerAndTest.sh @ONLY)
+    
+    add_test(NAME ProviderNameTester COMMAND ${CMAKE_CURRENT_BINARY_DIR}/ProviderNameStartServerAndTest.sh)
+    set_tests_properties(ProviderNameTester PROPERTIES TIMEOUT 120)
+
+3. Create a shell script to start the mock server and run tests:
+
+.. code-block:: bash
+
+    #!/bin/bash
+    
+    # Start the mock server
+    python3 -m utils.mock_qpu.<provider_name> @PORT@ &
+    SERVER_PID=$!
+    
+    # Wait for server to start
+    sleep 2
+    
+    # Run the test
+    @CMAKE_CURRENT_BINARY_DIR@/ProviderNameTester
+    TEST_STATUS=$?
+    
+    # Kill the server
+    kill $SERVER_PID
+    
+    # Return the test status
+    exit $TEST_STATUS
+
+4. Implement the C++ test:
+
+.. code-block:: cpp
+
+    // ProviderNameTester.cpp
+    #include "common/Logger.h"
+    #include "common/RestClient.h"
+    #include "common/ServerHelper.h"
+    #include "cudaq/platform/quantum_platform.h"
+    #include "gtest/gtest.h"
+    
+    TEST(ProviderNameTester, checkSimpleCircuit) {
+      // Initialize the platform
+      auto platform = cudaq::get_platform();
+      platform->setTargetBackend("<provider_name>");
+      
+      // Set configuration
+      platform->setBackendParameter("url", "http://localhost:PORT");
+      platform->setBackendParameter("api_key", "test_key");
+      
+      // Create a simple circuit
+      auto kernel = cudaq::make_kernel();
+      auto qubits = kernel.qalloc(2);
+      kernel.h(qubits[0]);
+      kernel.cx(qubits[0], qubits[1]);
+      kernel.mz(qubits);
+      
+      // Execute the circuit
+      auto counts = cudaq::sample(kernel);
+      
+      // Check results
+      EXPECT_EQ(counts.size(), 2);
+      EXPECT_TRUE(counts.has_key("00"));
+      EXPECT_TRUE(counts.has_key("11"));
+    }
+
+To make sure the C++ tests don't run if your target is not enabled, add the following to ``targettests/lit.site.cfg.py.in``:
+
+.. code-block:: python
+
+    config.cudaq_backends_provider = "@CUDAQ_ENABLE_PROVIDER_NAME_BACKEND@"
+    if cmake_boolvar_to_bool(config.cudaq_backends_provider):
+        config.available_features.add('provider')
+        config.substitutions.append(('%provider_avail', 'true'))
+    else:
+        config.substitutions.append(('%provider_avail', 'false'))
+
+And add the following to your ``targettests`` ``.cpp`` file:
+
+.. code-block:: cpp
+
+    // RUN: if %provider_avail; then nvq++ %cpp_std --target provider %s -o %t.x; fi
+
+Mock Server
+-----------
+
+Create a mock server for testing:
+
+.. code-block:: text
+
+    utils/mock_qpu/<provider_name>/
+    └── __init__.py
+
+Implement the mock server:
+
+.. code-block:: python
+
+    # __init__.py
+    from http.server import BaseHTTPRequestHandler, HTTPServer
+    import json
+    import sys
+    import time
+    
+    class ProviderNameMockServer(BaseHTTPRequestHandler):
+        def _set_headers(self, status_code=200):
+            self.send_response(status_code)
+            self.send_header('Content-type', 'application/json')
+            self.end_headers()
+    
+        def do_POST(self):
+            content_length = int(self.headers['Content-Length'])
+            post_data = self.rfile.read(content_length)
+            data = json.loads(post_data.decode('utf-8'))
+            
+            if self.path == '/jobs':
+                # Create a job
+                response = {
+                    'id': 'job-123',
+                    'status': 'QUEUED'
+                }
+                self._set_headers()
+                self.wfile.write(json.dumps(response).encode())
+            else:
+                self._set_headers(404)
+                self.wfile.write(json.dumps({'error': 'Not found'}).encode())
+    
+        def do_GET(self):
+            if self.path.startswith('/jobs/job-123'):
+                # Return job status and results
+                response = {
+                    'id': 'job-123',
+                    'status': 'COMPLETED',
+                    'results': {
+                        'counts': {
+                            '00': 500,
+                            '11': 500
+                        }
+                    }
+                }
+                self._set_headers()
+                self.wfile.write(json.dumps(response).encode())
+            else:
+                self._set_headers(404)
+                self.wfile.write(json.dumps({'error': 'Not found'}).encode())
+    
+    def startServer(port=8000):
+        server_address = ('', port)
+        httpd = HTTPServer(server_address, ProviderNameMockServer)
+        print(f'Starting mock server on port {port}...')
+        httpd.serve_forever()
+    
+    if __name__ == '__main__':
+        port = int(sys.argv[1]) if len(sys.argv) > 1 else 8000
+        startServer(port)
+
+Python Tests
+------------
+
+Create Python tests for your backend:
+
+.. code-block:: python
+
+    # python/tests/backends/test_<provider_name>.py
+    import os
+    import sys
+    import time
+    import pytest
+    from multiprocessing import Process
+    
+    import cudaq
+    from cudaq import spin
+
+    skipIf<provider_name>NotInstalled = pytest.mark.skipif(
+        not (cudaq.has_target("<provider_name>")),
+        reason='Could not find `<provider_name>` in installation')
+    
+    try:
+        from utils.mock_qpu.<provider_name> import startServer
+    except:
+        print("Mock qpu not available, skipping Provider Name tests.")
+        pytest.skip("Mock qpu not available.", allow_module_level=True)
+    
+    # Define the port for the mock server - make sure this is unique
+    # across all tests.
+    port = 62444
+    
+    @pytest.fixture(scope="session", autouse=True)
+    def startUpMockServer():
+        # Set the targeted QPU
+        cudaq.set_target('<provider_name>',
+                        url=f'http://localhost:{port}',
+                        api_key="test_key")
+        
+        # Launch the Mock Server
+        p = Process(target=startServer, args=(port,))
+        p.start()
+        time.sleep(1)
+        
+        yield "Running the tests."
+        
+        # Kill the server
+        p.terminate()
+    
+    def test_<provider_name>_sample():
+        # Create the kernel
+        kernel = cudaq.make_kernel()
+        qubits = kernel.qalloc(2)
+        kernel.h(qubits[0])
+        kernel.cx(qubits[0], qubits[1])
+        kernel.mz(qubits)
+        
+        # Run sample
+        counts = cudaq.sample(kernel)
+        assert len(counts) == 2
+        assert '00' in counts
+        assert '11' in counts
+        
+        # Run sample asynchronously
+        future = cudaq.sample_async(kernel)
+        counts = future.get()
+        assert len(counts) == 2
+        assert '00' in counts
+        assert '11' in counts
+
+Integration Tests
+-----------------
+
+To ensure proper execution on the hardware, a validation backend must be provided that:
+
+1. Consumes the same format that your target will use
+2. Validates that circuits passing this validation will execute successfully on the actual hardware
+3. Can be accessed by CUDA-Q's GitHub CI/CD pipelines
+
+Your validation backend doesn't need to be publicly available, but it should:
+
+- Accept the same input format as your actual quantum processor
+- Return meaningful error messages for invalid circuits
+- Provide an API endpoint that can be called from our integration tests
+
+If your validation backend is not publicly available, please coordinate the exchange of necessary credentials for CI/CD with the CUDA-Q team.
+
+Add your target to ``.github/workflows/integration_tests.yml``:
+
+.. code-block:: yaml
+
+    - name: Submit to <provider_name> test server
+      if: (success() || failure()) && (inputs.target == '<provider_name>' || github.event_name == 'schedule')
+      run: |
+        echo "### Submit to <provider_name> server" >> $GITHUB_STEP_SUMMARY
+        # Set up any required dependencies        
+        # Set up environment variables for authentication
+        export PROVIDER_API_KEY='${{ secrets.PROVIDER_API_KEY }}'
+        
+        # Run the integration tests
+        python_tests="docs/sphinx/targets/python/<provider_name>.py"
+        cpp_tests="docs/sphinx/targets/cpp/<provider_name>.cpp"
+        
+        # Execute tests (see other provider examples for implementation details)
+        # ...
+
+Documentation
+=============
+
+Add documentation for your backend in the appropriate sections of the CUDA-Q documentation. This should include:
+
+1. How to access your server (authentication set up, documentation etc.)
+2. How to configure and use the backend
+3. Any provider-specific parameters or features
+4. Examples of running circuits on the backend
+5. Adding your logo to the diagram on :doc:`../backends/hardware`
+
+More specifically, you will need to modify at least the following files:
+* ``docs/sphinx/using/examples/hardware_providers.rst``
+* ``docs/sphinx/using/backends/hardware.rst``
+* ``docs/sphinx/using/backends/hardware/<your-technology>.rst``
+* ``docs/sphinx/targets/python/<provider_name>.py``
+* ``docs/sphinx/targets/cpp/<provider_name>.cpp``
+
+Example Usage
+=============
+
+Once your backend is implemented, users can use it as follows:
+
+.. code-block:: python
+
+    import cudaq
+    
+    # Set the target to your provider
+    cudaq.set_target('<provider_name>', 
+                    api_key='your_api_key',
+                    device='your_device')
+    
+    # Create and run a circuit
+    @cudaq.kernel
+    def bell():
+        qubits = cudaq.qvector(2)
+        h(qubits[0])
+        x.ctrl(qubits[0], qubits[1])
+        mz(qubits)
+    
+    # Run the circuit
+    counts = cudaq.sample(bell)
+    print(counts)
+
+Code Review
+===========
+
+Once you have implemented a ``BackendHelper``, some basic tests, and documentation, please `create a PR <https://github.com/NVIDIA/cuda-quantum/pulls>`_ with your changes and tag the CUDA-Q team for review.
path: docs/sphinx/using/extending/backend.rst
user: login: khalatepradnya
body: `BackendHelper` term is not used anywhere else, perhaps use `ProviderNameServerHelper` for consistency?
created_at: 2025-05-16T21:29:53Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,622 @@
+********************************************
+Extending CUDA-Q with a new Hardware Backend
+********************************************
+
+This guide explains how to integrate a new quantum hardware provider with CUDA-Q. The integration process involves creating a server helper that handles communication with the provider's API, defining configuration files, and implementing necessary tests.
+
+Overview
+========
+
+CUDA-Q supports various quantum hardware :doc:`backends <../backends/hardware>` through a REST-based architecture. To add a new backend, you'll need to:
+
+1. Create a server helper class that handles API communication
+2. Define configuration files for the target
+3. Implement tests to verify functionality
+4. Add documentation for users
+
+The following sections detail each step of this process.
+
+Server Helper Implementation
+============================
+
+The server helper is the core component that handles communication with the quantum hardware provider's API. It extends the ``ServerHelper`` base class and implements methods for job submission, result retrieval, and other provider-specific functionality. The base class definition can be found in the `CUDA-Q repository <https://github.com/NVIDIA/cuda-quantum/blob/main/runtime/common/ServerHelper.h>`_.
+
+Directory Structure
+-------------------
+
+Create the following directory structure for your new backend (replace ``<provider_name>`` with your provider's name):
+
+.. code-block:: text
+
+    runtime/cudaq/platform/default/rest/helpers/<provider_name>/
+    ├── CMakeLists.txt
+    ├── ProviderNameServerHelper.cpp
+    └── <provider_name>.yml
+
+Server Helper Class
+-------------------
+
+Here's a template for implementing a server helper class:
+
+.. code-block:: cpp
+
+    // ProviderNameServerHelper.cpp
+    #include "common/Logger.h"
+    #include "common/RestClient.h"
+    #include "common/ServerHelper.h"
+    #include "cudaq/Support/Version.h"
+    #include "cudaq/utils/cudaq_utils.h"
+    #include <bitset>
+    #include <fstream>
+    #include <iostream>
+    #include <map>
+    #include <regex>
+    #include <sstream>
+    #include <thread>
+    #include <unordered_set>
+    
+    using json = nlohmann::json;
+    
+    namespace cudaq {
+    
+    /// @brief The ProviderNameServerHelper class extends the ServerHelper class
+    /// to handle interactions with the Provider Name server for submitting and
+    /// retrieving quantum computation jobs.
+    class ProviderNameServerHelper : public ServerHelper {
+      static constexpr const char *DEFAULT_URL = "https://api.provider-name.com";
+      static constexpr const char *DEFAULT_VERSION = "v1.0";
+    
+    public:
+      const std::string name() const override { return "<provider_name>"; }
+    
+      /// @brief Example implementation of authentication headers.
+      RestHeaders getHeaders() override {
+        RestHeaders headers;
+        headers["Content-Type"] = "application/json";
+        
+        // Add authentication headers if needed
+        if (backendConfig.count("api_key"))
+          headers["Authorization"] = "Bearer " + backendConfig["api_key"];
+        
+        return headers;
+      }
+    
+      /// @brief Example implementation of backend initialization.
+      void initialize(BackendConfig config) override {
+        cudaq::info("Initializing Provider Name Backend");
+        backendConfig = config;
+        
+        if (!backendConfig.count("url"))
+          backendConfig["url"] = DEFAULT_URL;
+        if (!backendConfig.count("version"))
+          backendConfig["version"] = DEFAULT_VERSION;
+
+        // Set shots if provided
+        if (config.find("shots") != config.end())
+          this->setShots(std::stoul(config["shots"]));
+      }
+    
+      /// @brief Example implementation of simple job creation.
+      ServerJobPayload createJob(std::vector<KernelExecution> &circuitCodes) override {
+        ServerMessage job;
+        job["content"] = circuitCodes[0].code;
+        job["shots"] = shots;
+        
+        RestHeaders headers = getHeaders();
+        std::string path = "/jobs";
+        
+        return std::make_tuple(backendConfig["url"] + path, headers, 
+                              std::vector<ServerMessage>{job});
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string extractJobId(ServerMessage &postResponse) override {
+        if (!postResponse.contains("id"))
+          return "";
+        
+        return postResponse.at("id");
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string constructGetJobPath(ServerMessage &postResponse) override {
+        return extractJobId(postResponse);
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string constructGetJobPath(std::string &jobId) override {
+        return backendConfig["url"] + "/jobs/" + jobId;
+      }
+    
+      /// @brief Example implementation of job status checking.
+      bool jobIsDone(ServerMessage &getJobResponse) override {
+        if (!getJobResponse.contains("status"))
+          return false;
+        
+        std::string status = getJobResponse["status"];
+        return status == "COMPLETED" || status == "FAILED";
+      }
+    
+      /// @brief Example implementation of result processing.
+      ///
+      /// The raw results from quantum hardware often need post-processing (bit
+      /// reordering, normalization, etc.) to match CUDA-Q's expectations.
+      /// This is the place to do that.
+      cudaq::sample_result processResults(ServerMessage &getJobResponse,
+                                         std::string &jobId) override {
+        cudaq::info("Processing results: {}", getJobResponse.dump());
+        
+        // Extract measurement results from the response
+        auto samplesJson = getJobResponse["results"]["counts"];
+        cudaq::CountsDictionary counts;
+        
+        for (auto &item : samplesJson.items()) {
+          std::string bitstring = item.key();
+          std::size_t count = item.value();
+          counts[bitstring] = count;
+        }
+        
+        // Create an ExecutionResult
+        cudaq::ExecutionResult execResult{counts};
+        
+        // Return the sample_result
+        return cudaq::sample_result{execResult};
+      }
+    
+      /// @brief Example implementation of polling configuration.
+      std::chrono::microseconds
+      nextResultPollingInterval(ServerMessage &postResponse) override {
+        return std::chrono::seconds(5);
+      }
+    };
+    
+    } // namespace cudaq
+    
+    // Register the server helper in the CUDA-Q server helper factory
+    CUDAQ_REGISTER_TYPE(cudaq::ServerHelper, cudaq::ProviderNameServerHelper, <provider_name>)
+
+``CMakeLists.txt``
+------------------
+
+You will need to configure CUDA-Q's ``cmake`` system for your new server helper. By convention, you should setup your target as optional by adding a CMake flag in the ``CMakeLists.txt`` at the root of the CUDA-Q repository:
+
+.. code-block:: cmake
+
+    # Enable <provider_name> target by default
+    if (NOT DEFINED CUDAQ_ENABLE_PROVIDER_NAME_BACKEND)
+      set(CUDAQ_ENABLE_PROVIDER_NAME_BACKEND ON CACHE BOOL "Enable building the <Provider Name> target.")
+    endif()
+
+Then, create a ``CMakeLists.txt`` file in your server helper's directory and check for this flag:
+
+.. code-block:: cmake
+
+    if(CUDAQ_ENABLE_PROVIDER_NAME_BACKEND)
+      target_sources(cudaq-rest-qpu PRIVATE ProviderNameServerHelper.cpp)
+      add_target_config(<provider_name>)
+      
+      add_library(cudaq-serverhelper-<provider_name> SHARED ProviderNameServerHelper.cpp)
+      target_link_libraries(cudaq-serverhelper-<provider_name>
+        PUBLIC
+        cudaq-common
+        fmt::fmt-header-only
+      )
+      install(TARGETS cudaq-serverhelper-<provider_name> DESTINATION lib)
+    endif()
+
+Target Configuration
+====================
+
+Create a ``YAML`` configuration file for your target:
+
+.. code-block:: yaml
+
+    # <provider_name>.yml
+    name: "<provider_name>"
+    description: "CUDA-Q target for Provider Name."
+    
+    config:
+      # Tell DefaultQuantumPlatform what QPU subtype to use
+      platform-qpu: remote_rest
+      # Add the rest-qpu library to the link list
+      link-libs: ["-lcudaq-rest-qpu"]
+      # Tell NVQ++ to generate glue code to set the target backend name
+      gen-target-backend: true
+      # Add preprocessor defines to compilation
+      preprocessor-defines: ["-D CUDAQ_QUANTUM_DEVICE"]
+      # Define the lowering pipeline
+      # This will cover applying hardware-specific constraints since each provider may have different native gate sets, requiring custom mappings and decompositions. You may need assistance from the CUDA-Q team to set this up correctly.
+      platform-lowering-config: "classical-optimization-pipeline,globalize-array-values,func.func(state-prep),unitary-synthesis,canonicalize,apply-op-specialization,aggressive-early-inlining,classical-optimization-pipeline,func.func(lower-to-cfg,canonicalize,multicontrol-decomposition),decomposition{enable-patterns=SToR1,TToR1,R1ToU3,U3ToRotations,CHToCX,CCZToCX,CRzToCX,CRyToCX,CRxToCX,CR1ToCX},quake-to-cc-prep,func.func(expand-control-veqs,combine-quantum-alloc,canonicalize,combine-measurements),symbol-dce"
+      # Tell the rest-qpu that we are generating QIR base profile.
+      # As of the time of this writing, qasm2, qir-base and qir-adaptive are supported.
+      codegen-emission: qir-base
+      # Library mode is only for simulators, physical backends must turn this off
+      library-mode: false
+    
+    # Some examples of target arguments are shown below.
+    # You do not need to add any arguments for your backend if you do not need them.
+    target-arguments:
+      - key: api-key
+        required: true
+        type: string
+        platform-arg: api_key
+        help-string: "API key for Provider Name."
+      - key: url
+        required: false
+        type: string
+        platform-arg: url
+        help-string: "Specify Provider Name API server URL."
+      - key: device
+        required: false
+        type: string
+        platform-arg: device
+        help-string: "Specify the Provider Name quantum device to use."
+
+Update Parent ``CMakeLists.txt``
+--------------------------------
+
+Add your provider to the parent ``CMakeLists.txt`` file:
+
+.. code-block:: cmake
+
+    # runtime/cudaq/platform/default/rest/helpers/CMakeLists.txt
+    add_subdirectory(<provider_name>)
+    add_subdirectory(ionq)
+    add_subdirectory(iqm)
+    # ... other providers
+
+Testing
+=======
+
+Unit Tests
+----------
+
+Create unit tests for your server helper:
+
+1. Create a directory structure:
+
+.. code-block:: text
+
+    unittests/backends/<provider_name>/
+    ├── CMakeLists.txt
+    ├── ProviderNameStartServerAndTest.sh.in
+    └── ProviderNameTester.cpp
+
+2. Implement the test files:
+
+.. code-block:: cmake
+
+    # CMakeLists.txt
+    add_executable(ProviderNameTester ProviderNameTester.cpp)
+    target_link_libraries(ProviderNameTester
+      PRIVATE
+      cudaq-common
+      cudaq
+      gtest_main
+    )
+    
+    configure_file(${CMAKE_CURRENT_SOURCE_DIR}/ProviderNameStartServerAndTest.sh.in
+                  ${CMAKE_CURRENT_BINARY_DIR}/ProviderNameStartServerAndTest.sh @ONLY)
+    
+    add_test(NAME ProviderNameTester COMMAND ${CMAKE_CURRENT_BINARY_DIR}/ProviderNameStartServerAndTest.sh)
+    set_tests_properties(ProviderNameTester PROPERTIES TIMEOUT 120)
+
+3. Create a shell script to start the mock server and run tests:
+
+.. code-block:: bash
+
+    #!/bin/bash
+    
+    # Start the mock server
+    python3 -m utils.mock_qpu.<provider_name> @PORT@ &
+    SERVER_PID=$!
+    
+    # Wait for server to start
+    sleep 2
+    
+    # Run the test
+    @CMAKE_CURRENT_BINARY_DIR@/ProviderNameTester
+    TEST_STATUS=$?
+    
+    # Kill the server
+    kill $SERVER_PID
+    
+    # Return the test status
+    exit $TEST_STATUS
+
+4. Implement the C++ test:
+
+.. code-block:: cpp
+
+    // ProviderNameTester.cpp
+    #include "common/Logger.h"
+    #include "common/RestClient.h"
+    #include "common/ServerHelper.h"
+    #include "cudaq/platform/quantum_platform.h"
+    #include "gtest/gtest.h"
+    
+    TEST(ProviderNameTester, checkSimpleCircuit) {
+      // Initialize the platform
+      auto platform = cudaq::get_platform();
+      platform->setTargetBackend("<provider_name>");
+      
+      // Set configuration
+      platform->setBackendParameter("url", "http://localhost:PORT");
+      platform->setBackendParameter("api_key", "test_key");
+      
+      // Create a simple circuit
+      auto kernel = cudaq::make_kernel();
+      auto qubits = kernel.qalloc(2);
+      kernel.h(qubits[0]);
+      kernel.cx(qubits[0], qubits[1]);
+      kernel.mz(qubits);
+      
+      // Execute the circuit
+      auto counts = cudaq::sample(kernel);
+      
+      // Check results
+      EXPECT_EQ(counts.size(), 2);
+      EXPECT_TRUE(counts.has_key("00"));
+      EXPECT_TRUE(counts.has_key("11"));
+    }
+
+To make sure the C++ tests don't run if your target is not enabled, add the following to ``targettests/lit.site.cfg.py.in``:
+
+.. code-block:: python
+
+    config.cudaq_backends_provider = "@CUDAQ_ENABLE_PROVIDER_NAME_BACKEND@"
+    if cmake_boolvar_to_bool(config.cudaq_backends_provider):
+        config.available_features.add('provider')
+        config.substitutions.append(('%provider_avail', 'true'))
+    else:
+        config.substitutions.append(('%provider_avail', 'false'))
+
+And add the following to your ``targettests`` ``.cpp`` file:
+
+.. code-block:: cpp
+
+    // RUN: if %provider_avail; then nvq++ %cpp_std --target provider %s -o %t.x; fi
+
+Mock Server
+-----------
+
+Create a mock server for testing:
+
+.. code-block:: text
+
+    utils/mock_qpu/<provider_name>/
+    └── __init__.py
+
+Implement the mock server:
+
+.. code-block:: python
+
+    # __init__.py
+    from http.server import BaseHTTPRequestHandler, HTTPServer
+    import json
+    import sys
+    import time
+    
+    class ProviderNameMockServer(BaseHTTPRequestHandler):
+        def _set_headers(self, status_code=200):
+            self.send_response(status_code)
+            self.send_header('Content-type', 'application/json')
+            self.end_headers()
+    
+        def do_POST(self):
+            content_length = int(self.headers['Content-Length'])
+            post_data = self.rfile.read(content_length)
+            data = json.loads(post_data.decode('utf-8'))
+            
+            if self.path == '/jobs':
+                # Create a job
+                response = {
+                    'id': 'job-123',
+                    'status': 'QUEUED'
+                }
+                self._set_headers()
+                self.wfile.write(json.dumps(response).encode())
+            else:
+                self._set_headers(404)
+                self.wfile.write(json.dumps({'error': 'Not found'}).encode())
+    
+        def do_GET(self):
+            if self.path.startswith('/jobs/job-123'):
+                # Return job status and results
+                response = {
+                    'id': 'job-123',
+                    'status': 'COMPLETED',
+                    'results': {
+                        'counts': {
+                            '00': 500,
+                            '11': 500
+                        }
+                    }
+                }
+                self._set_headers()
+                self.wfile.write(json.dumps(response).encode())
+            else:
+                self._set_headers(404)
+                self.wfile.write(json.dumps({'error': 'Not found'}).encode())
+    
+    def startServer(port=8000):
+        server_address = ('', port)
+        httpd = HTTPServer(server_address, ProviderNameMockServer)
+        print(f'Starting mock server on port {port}...')
+        httpd.serve_forever()
+    
+    if __name__ == '__main__':
+        port = int(sys.argv[1]) if len(sys.argv) > 1 else 8000
+        startServer(port)
+
+Python Tests
+------------
+
+Create Python tests for your backend:
+
+.. code-block:: python
+
+    # python/tests/backends/test_<provider_name>.py
+    import os
+    import sys
+    import time
+    import pytest
+    from multiprocessing import Process
+    
+    import cudaq
+    from cudaq import spin
+
+    skipIf<provider_name>NotInstalled = pytest.mark.skipif(
+        not (cudaq.has_target("<provider_name>")),
+        reason='Could not find `<provider_name>` in installation')
+    
+    try:
+        from utils.mock_qpu.<provider_name> import startServer
+    except:
+        print("Mock qpu not available, skipping Provider Name tests.")
+        pytest.skip("Mock qpu not available.", allow_module_level=True)
+    
+    # Define the port for the mock server - make sure this is unique
+    # across all tests.
+    port = 62444
+    
+    @pytest.fixture(scope="session", autouse=True)
+    def startUpMockServer():
+        # Set the targeted QPU
+        cudaq.set_target('<provider_name>',
+                        url=f'http://localhost:{port}',
+                        api_key="test_key")
+        
+        # Launch the Mock Server
+        p = Process(target=startServer, args=(port,))
+        p.start()
+        time.sleep(1)
+        
+        yield "Running the tests."
+        
+        # Kill the server
+        p.terminate()
+    
+    def test_<provider_name>_sample():
+        # Create the kernel
+        kernel = cudaq.make_kernel()
path: docs/sphinx/using/extending/backend.rst
user: login: nvidia-dobri
body: It's just the overwhelming majority of our existing tests:
https://github.com/NVIDIA/cuda-quantum/blob/main/unittests/backends/infleqtion/InfleqtionTester.cpp
https://github.com/NVIDIA/cuda-quantum/blob/main/unittests/backends/anyon/AnyonTester.cpp
https://github.com/NVIDIA/cuda-quantum/blob/main/unittests/backends/ionq/IonQTester.cpp
https://github.com/NVIDIA/cuda-quantum/blob/main/unittests/backends/oqc/OQCTester.cpp
...etc

Is there a reason to avoid the dynamic kernel builder?
created_at: 2025-05-16T21:55:50Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,622 @@
+********************************************
+Extending CUDA-Q with a new Hardware Backend
+********************************************
+
+This guide explains how to integrate a new quantum hardware provider with CUDA-Q. The integration process involves creating a server helper that handles communication with the provider's API, defining configuration files, and implementing necessary tests.
+
+Overview
+========
+
+CUDA-Q supports various quantum hardware :doc:`backends <../backends/hardware>` through a REST-based architecture. To add a new backend, you'll need to:
+
+1. Create a server helper class that handles API communication
+2. Define configuration files for the target
+3. Implement tests to verify functionality
+4. Add documentation for users
+
+The following sections detail each step of this process.
+
+Server Helper Implementation
+============================
+
+The server helper is the core component that handles communication with the quantum hardware provider's API. It extends the ``ServerHelper`` base class and implements methods for job submission, result retrieval, and other provider-specific functionality. The base class definition can be found in the `CUDA-Q repository <https://github.com/NVIDIA/cuda-quantum/blob/main/runtime/common/ServerHelper.h>`_.
+
+Directory Structure
+-------------------
+
+Create the following directory structure for your new backend (replace ``<provider_name>`` with your provider's name):
+
+.. code-block:: text
+
+    runtime/cudaq/platform/default/rest/helpers/<provider_name>/
+    ├── CMakeLists.txt
+    ├── ProviderNameServerHelper.cpp
+    └── <provider_name>.yml
+
+Server Helper Class
+-------------------
+
+Here's a template for implementing a server helper class:
+
+.. code-block:: cpp
+
+    // ProviderNameServerHelper.cpp
+    #include "common/Logger.h"
+    #include "common/RestClient.h"
+    #include "common/ServerHelper.h"
+    #include "cudaq/Support/Version.h"
+    #include "cudaq/utils/cudaq_utils.h"
+    #include <bitset>
+    #include <fstream>
+    #include <iostream>
+    #include <map>
+    #include <regex>
+    #include <sstream>
+    #include <thread>
+    #include <unordered_set>
+    
+    using json = nlohmann::json;
+    
+    namespace cudaq {
+    
+    /// @brief The ProviderNameServerHelper class extends the ServerHelper class
+    /// to handle interactions with the Provider Name server for submitting and
+    /// retrieving quantum computation jobs.
+    class ProviderNameServerHelper : public ServerHelper {
+      static constexpr const char *DEFAULT_URL = "https://api.provider-name.com";
+      static constexpr const char *DEFAULT_VERSION = "v1.0";
+    
+    public:
+      const std::string name() const override { return "<provider_name>"; }
+    
+      /// @brief Example implementation of authentication headers.
+      RestHeaders getHeaders() override {
+        RestHeaders headers;
+        headers["Content-Type"] = "application/json";
+        
+        // Add authentication headers if needed
+        if (backendConfig.count("api_key"))
+          headers["Authorization"] = "Bearer " + backendConfig["api_key"];
+        
+        return headers;
+      }
+    
+      /// @brief Example implementation of backend initialization.
+      void initialize(BackendConfig config) override {
+        cudaq::info("Initializing Provider Name Backend");
+        backendConfig = config;
+        
+        if (!backendConfig.count("url"))
+          backendConfig["url"] = DEFAULT_URL;
+        if (!backendConfig.count("version"))
+          backendConfig["version"] = DEFAULT_VERSION;
+
+        // Set shots if provided
+        if (config.find("shots") != config.end())
+          this->setShots(std::stoul(config["shots"]));
+      }
+    
+      /// @brief Example implementation of simple job creation.
+      ServerJobPayload createJob(std::vector<KernelExecution> &circuitCodes) override {
+        ServerMessage job;
+        job["content"] = circuitCodes[0].code;
+        job["shots"] = shots;
+        
+        RestHeaders headers = getHeaders();
+        std::string path = "/jobs";
+        
+        return std::make_tuple(backendConfig["url"] + path, headers, 
+                              std::vector<ServerMessage>{job});
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string extractJobId(ServerMessage &postResponse) override {
+        if (!postResponse.contains("id"))
+          return "";
+        
+        return postResponse.at("id");
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string constructGetJobPath(ServerMessage &postResponse) override {
+        return extractJobId(postResponse);
+      }
+    
+      /// @brief Example implementation of job ID tracking.
+      std::string constructGetJobPath(std::string &jobId) override {
+        return backendConfig["url"] + "/jobs/" + jobId;
+      }
+    
+      /// @brief Example implementation of job status checking.
+      bool jobIsDone(ServerMessage &getJobResponse) override {
+        if (!getJobResponse.contains("status"))
+          return false;
+        
+        std::string status = getJobResponse["status"];
+        return status == "COMPLETED" || status == "FAILED";
+      }
+    
+      /// @brief Example implementation of result processing.
+      ///
+      /// The raw results from quantum hardware often need post-processing (bit
+      /// reordering, normalization, etc.) to match CUDA-Q's expectations.
+      /// This is the place to do that.
+      cudaq::sample_result processResults(ServerMessage &getJobResponse,
+                                         std::string &jobId) override {
+        cudaq::info("Processing results: {}", getJobResponse.dump());
+        
+        // Extract measurement results from the response
+        auto samplesJson = getJobResponse["results"]["counts"];
+        cudaq::CountsDictionary counts;
+        
+        for (auto &item : samplesJson.items()) {
+          std::string bitstring = item.key();
+          std::size_t count = item.value();
+          counts[bitstring] = count;
+        }
+        
+        // Create an ExecutionResult
+        cudaq::ExecutionResult execResult{counts};
+        
+        // Return the sample_result
+        return cudaq::sample_result{execResult};
+      }
+    
+      /// @brief Example implementation of polling configuration.
+      std::chrono::microseconds
+      nextResultPollingInterval(ServerMessage &postResponse) override {
+        return std::chrono::seconds(5);
+      }
+    };
+    
+    } // namespace cudaq
+    
+    // Register the server helper in the CUDA-Q server helper factory
+    CUDAQ_REGISTER_TYPE(cudaq::ServerHelper, cudaq::ProviderNameServerHelper, <provider_name>)
+
+``CMakeLists.txt``
+------------------
+
+You will need to configure CUDA-Q's ``cmake`` system for your new server helper. By convention, you should setup your target as optional by adding a CMake flag in the ``CMakeLists.txt`` at the root of the CUDA-Q repository:
+
+.. code-block:: cmake
+
+    # Enable <provider_name> target by default
+    if (NOT DEFINED CUDAQ_ENABLE_PROVIDER_NAME_BACKEND)
+      set(CUDAQ_ENABLE_PROVIDER_NAME_BACKEND ON CACHE BOOL "Enable building the <Provider Name> target.")
+    endif()
+
+Then, create a ``CMakeLists.txt`` file in your server helper's directory and check for this flag:
+
+.. code-block:: cmake
+
+    if(CUDAQ_ENABLE_PROVIDER_NAME_BACKEND)
+      target_sources(cudaq-rest-qpu PRIVATE ProviderNameServerHelper.cpp)
+      add_target_config(<provider_name>)
+      
+      add_library(cudaq-serverhelper-<provider_name> SHARED ProviderNameServerHelper.cpp)
+      target_link_libraries(cudaq-serverhelper-<provider_name>
+        PUBLIC
+        cudaq-common
+        fmt::fmt-header-only
+      )
+      install(TARGETS cudaq-serverhelper-<provider_name> DESTINATION lib)
+    endif()
+
+Target Configuration
+====================
+
+Create a ``YAML`` configuration file for your target:
+
+.. code-block:: yaml
+
+    # <provider_name>.yml
+    name: "<provider_name>"
+    description: "CUDA-Q target for Provider Name."
+    
+    config:
+      # Tell DefaultQuantumPlatform what QPU subtype to use
+      platform-qpu: remote_rest
+      # Add the rest-qpu library to the link list
+      link-libs: ["-lcudaq-rest-qpu"]
+      # Tell NVQ++ to generate glue code to set the target backend name
+      gen-target-backend: true
+      # Add preprocessor defines to compilation
+      preprocessor-defines: ["-D CUDAQ_QUANTUM_DEVICE"]
+      # Define the lowering pipeline
+      # This will cover applying hardware-specific constraints since each provider may have different native gate sets, requiring custom mappings and decompositions. You may need assistance from the CUDA-Q team to set this up correctly.
+      platform-lowering-config: "classical-optimization-pipeline,globalize-array-values,func.func(state-prep),unitary-synthesis,canonicalize,apply-op-specialization,aggressive-early-inlining,classical-optimization-pipeline,func.func(lower-to-cfg,canonicalize,multicontrol-decomposition),decomposition{enable-patterns=SToR1,TToR1,R1ToU3,U3ToRotations,CHToCX,CCZToCX,CRzToCX,CRyToCX,CRxToCX,CR1ToCX},quake-to-cc-prep,func.func(expand-control-veqs,combine-quantum-alloc,canonicalize,combine-measurements),symbol-dce"
+      # Tell the rest-qpu that we are generating QIR base profile.
+      # As of the time of this writing, qasm2, qir-base and qir-adaptive are supported.
+      codegen-emission: qir-base
+      # Library mode is only for simulators, physical backends must turn this off
+      library-mode: false
+    
+    # Some examples of target arguments are shown below.
+    # You do not need to add any arguments for your backend if you do not need them.
+    target-arguments:
+      - key: api-key
+        required: true
+        type: string
+        platform-arg: api_key
+        help-string: "API key for Provider Name."
+      - key: url
+        required: false
+        type: string
+        platform-arg: url
+        help-string: "Specify Provider Name API server URL."
+      - key: device
+        required: false
+        type: string
+        platform-arg: device
+        help-string: "Specify the Provider Name quantum device to use."
+
+Update Parent ``CMakeLists.txt``
+--------------------------------
+
+Add your provider to the parent ``CMakeLists.txt`` file:
+
+.. code-block:: cmake
+
+    # runtime/cudaq/platform/default/rest/helpers/CMakeLists.txt
+    add_subdirectory(<provider_name>)
+    add_subdirectory(ionq)
+    add_subdirectory(iqm)
+    # ... other providers
+
+Testing
+=======
+
+Unit Tests
+----------
+
+Create unit tests for your server helper:
+
+1. Create a directory structure:
+
+.. code-block:: text
+
+    unittests/backends/<provider_name>/
+    ├── CMakeLists.txt
+    ├── ProviderNameStartServerAndTest.sh.in
+    └── ProviderNameTester.cpp
+
+2. Implement the test files:
+
+.. code-block:: cmake
+
+    # CMakeLists.txt
+    add_executable(ProviderNameTester ProviderNameTester.cpp)
+    target_link_libraries(ProviderNameTester
+      PRIVATE
+      cudaq-common
+      cudaq
+      gtest_main
+    )
+    
+    configure_file(${CMAKE_CURRENT_SOURCE_DIR}/ProviderNameStartServerAndTest.sh.in
+                  ${CMAKE_CURRENT_BINARY_DIR}/ProviderNameStartServerAndTest.sh @ONLY)
+    
+    add_test(NAME ProviderNameTester COMMAND ${CMAKE_CURRENT_BINARY_DIR}/ProviderNameStartServerAndTest.sh)
+    set_tests_properties(ProviderNameTester PROPERTIES TIMEOUT 120)
+
+3. Create a shell script to start the mock server and run tests:
+
+.. code-block:: bash
+
+    #!/bin/bash
+    
+    # Start the mock server
+    python3 -m utils.mock_qpu.<provider_name> @PORT@ &
+    SERVER_PID=$!
+    
+    # Wait for server to start
+    sleep 2
+    
+    # Run the test
+    @CMAKE_CURRENT_BINARY_DIR@/ProviderNameTester
+    TEST_STATUS=$?
+    
+    # Kill the server
+    kill $SERVER_PID
+    
+    # Return the test status
+    exit $TEST_STATUS
+
+4. Implement the C++ test:
+
+.. code-block:: cpp
+
+    // ProviderNameTester.cpp
+    #include "common/Logger.h"
+    #include "common/RestClient.h"
+    #include "common/ServerHelper.h"
+    #include "cudaq/platform/quantum_platform.h"
+    #include "gtest/gtest.h"
+    
+    TEST(ProviderNameTester, checkSimpleCircuit) {
+      // Initialize the platform
+      auto platform = cudaq::get_platform();
+      platform->setTargetBackend("<provider_name>");
+      
+      // Set configuration
+      platform->setBackendParameter("url", "http://localhost:PORT");
+      platform->setBackendParameter("api_key", "test_key");
+      
+      // Create a simple circuit
+      auto kernel = cudaq::make_kernel();
+      auto qubits = kernel.qalloc(2);
+      kernel.h(qubits[0]);
+      kernel.cx(qubits[0], qubits[1]);
+      kernel.mz(qubits);
+      
+      // Execute the circuit
+      auto counts = cudaq::sample(kernel);
+      
+      // Check results
+      EXPECT_EQ(counts.size(), 2);
+      EXPECT_TRUE(counts.has_key("00"));
+      EXPECT_TRUE(counts.has_key("11"));
+    }
+
+To make sure the C++ tests don't run if your target is not enabled, add the following to ``targettests/lit.site.cfg.py.in``:
+
+.. code-block:: python
+
+    config.cudaq_backends_provider = "@CUDAQ_ENABLE_PROVIDER_NAME_BACKEND@"
+    if cmake_boolvar_to_bool(config.cudaq_backends_provider):
+        config.available_features.add('provider')
+        config.substitutions.append(('%provider_avail', 'true'))
+    else:
+        config.substitutions.append(('%provider_avail', 'false'))
+
+And add the following to your ``targettests`` ``.cpp`` file:
+
+.. code-block:: cpp
+
+    // RUN: if %provider_avail; then nvq++ %cpp_std --target provider %s -o %t.x; fi
+
+Mock Server
+-----------
+
+Create a mock server for testing:
+
+.. code-block:: text
+
+    utils/mock_qpu/<provider_name>/
+    └── __init__.py
+
+Implement the mock server:
+
+.. code-block:: python
+
+    # __init__.py
+    from http.server import BaseHTTPRequestHandler, HTTPServer
+    import json
+    import sys
+    import time
+    
+    class ProviderNameMockServer(BaseHTTPRequestHandler):
+        def _set_headers(self, status_code=200):
+            self.send_response(status_code)
+            self.send_header('Content-type', 'application/json')
+            self.end_headers()
+    
+        def do_POST(self):
+            content_length = int(self.headers['Content-Length'])
+            post_data = self.rfile.read(content_length)
+            data = json.loads(post_data.decode('utf-8'))
+            
+            if self.path == '/jobs':
+                # Create a job
+                response = {
+                    'id': 'job-123',
+                    'status': 'QUEUED'
+                }
+                self._set_headers()
+                self.wfile.write(json.dumps(response).encode())
+            else:
+                self._set_headers(404)
+                self.wfile.write(json.dumps({'error': 'Not found'}).encode())
+    
+        def do_GET(self):
+            if self.path.startswith('/jobs/job-123'):
+                # Return job status and results
+                response = {
+                    'id': 'job-123',
+                    'status': 'COMPLETED',
+                    'results': {
+                        'counts': {
+                            '00': 500,
+                            '11': 500
+                        }
+                    }
+                }
+                self._set_headers()
+                self.wfile.write(json.dumps(response).encode())
+            else:
+                self._set_headers(404)
+                self.wfile.write(json.dumps({'error': 'Not found'}).encode())
+    
+    def startServer(port=8000):
+        server_address = ('', port)
+        httpd = HTTPServer(server_address, ProviderNameMockServer)
+        print(f'Starting mock server on port {port}...')
+        httpd.serve_forever()
+    
+    if __name__ == '__main__':
+        port = int(sys.argv[1]) if len(sys.argv) > 1 else 8000
+        startServer(port)
+
+Python Tests
+------------
+
+Create Python tests for your backend:
+
+.. code-block:: python
+
+    # python/tests/backends/test_<provider_name>.py
+    import os
+    import sys
+    import time
+    import pytest
+    from multiprocessing import Process
+    
+    import cudaq
+    from cudaq import spin
+
+    skipIf<provider_name>NotInstalled = pytest.mark.skipif(
+        not (cudaq.has_target("<provider_name>")),
+        reason='Could not find `<provider_name>` in installation')
+    
+    try:
+        from utils.mock_qpu.<provider_name> import startServer
+    except:
+        print("Mock qpu not available, skipping Provider Name tests.")
+        pytest.skip("Mock qpu not available.", allow_module_level=True)
+    
+    # Define the port for the mock server - make sure this is unique
+    # across all tests.
+    port = 62444
+    
+    @pytest.fixture(scope="session", autouse=True)
+    def startUpMockServer():
+        # Set the targeted QPU
+        cudaq.set_target('<provider_name>',
+                        url=f'http://localhost:{port}',
+                        api_key="test_key")
+        
+        # Launch the Mock Server
+        p = Process(target=startServer, args=(port,))
+        p.start()
+        time.sleep(1)
+        
+        yield "Running the tests."
+        
+        # Kill the server
+        p.terminate()
+    
+    def test_<provider_name>_sample():
+        # Create the kernel
+        kernel = cudaq.make_kernel()
path: docs/sphinx/using/extending/backend.rst
user: login: khalatepradnya
body: Kernel (MLIR) mode is preferable due to its expressiveness and composability.
created_at: 2025-05-16T22:00:53Z
author_association: COLLABORATOR

PR comments:
user: login: copy-pr-bot[bot]
created_at: 2025-05-07T15:39:01Z
body: This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities [here](https://docs.gha-runners.nvidia.com/cpr/vetters).

Contributors can view more details about this message [here](https://docs.gha-runners.nvidia.com/cpr/contributors).

author_association: NONE
user: login: github-actions[bot]
created_at: 2025-05-16T17:24:19Z
body: **CUDA Quantum Docs Bot:** A preview of the documentation can be found [here](https://htmlpreview.github.io/?https://github.com/NVIDIA/cuda-quantum/blob/docs/preview/pr-2878/index.html).
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-05-16T19:23:27Z
body: **CUDA Quantum Docs Bot:** A preview of the documentation can be found [here](https://htmlpreview.github.io/?https://github.com/NVIDIA/cuda-quantum/blob/docs/preview/pr-2878/index.html).
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-05-16T21:10:10Z
body: **CUDA Quantum Docs Bot:** A preview of the documentation can be found [here](https://htmlpreview.github.io/?https://github.com/NVIDIA/cuda-quantum/blob/docs/preview/pr-2878/index.html).
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-05-16T23:34:07Z
body: **CUDA Quantum Docs Bot:** A preview of the documentation can be found [here](https://htmlpreview.github.io/?https://github.com/NVIDIA/cuda-quantum/blob/docs/preview/pr-2878/index.html).
author_association: NONE