Table of Contents

Debugging and Profiling

Debugging quantum kernels involves printing intermediate results, validating against classical simulators, and profiling gate timing.

#include "cudaq.h"
#include <iostream>
 
struct DebugKernel {
  void operator()() __qpu__ {
    cudaq::qvector q(3);
 
    // Initialize
    h(q[0]);
    h(q[1]);
    h(q[2]);
 
    // Entangle
    cx(q[0], q[1]);
    cx(q[1], q[2]);
 
    // Measure
    mz(q[0], q[1], q[2]);
  }
};
 
int main() {
  // Test on CPU simulator (slower, more detailed debugging)
  cudaq::set_target("qpp");
  auto cpu_result = cudaq::sample<DebugKernel>(1000);
 
  printf("CPU simulator result:\n");
  for (const auto& [bits, count] : cpu_result) {
    printf("  %s: %lu (%.3f%%)\n", bits.c_str(), count, 100.0*count/1000);
  }
 
  // Test on GPU simulator (faster, less debug info)
  cudaq::set_target("nvidia");
  auto gpu_result = cudaq::sample<DebugKernel>(1000);
 
  printf("GPU simulator result:\n");
  for (const auto& [bits, count] : gpu_result) {
    printf("  %s: %lu (%.3f%%)\n", bits.c_str(), count, 100.0*count/1000);
  }
 
  // Compare: should be statistically similar
  printf("Results match: ");
  bool match = true;
  for (const auto& [bits, count] : cpu_result) {
    auto gpu_count = gpu_result.count(bits);
    // Allow ~5% statistical variation
    if (std::abs(double(count - gpu_count)) > 50) {
      match = false;
      break;
    }
  }
  printf("%s\n", match ? "YES" : "NO");
 
  return 0;
}

Best Practices

  1. Start simple: test on 2–3 qubits before scaling
  2. Compare simulators: CPU and GPU should give same statistics
  3. Validate classically: for small circuits, verify results by hand
  4. Profile gates: measure gate times to identify bottlenecks

Debugging quantum code is different from classical—use statistical validation and classical fallbacks.