NVIDIA / cuda-quantum

C++ and Python support for the CUDA Quantum programming model for heterogeneous quantum-classical workflows
https://nvidia.github.io/cuda-quantum/
Other
468 stars 172 forks source link

Assertion in `vqe_h2.cpp` example with IonQ-emulate target #530

Closed 1tnguyen closed 10 months ago

1tnguyen commented 1 year ago

Required prerequisites

Describe the bug

When trying the vqe_h2.cpp example (cuda-quantum/docs/sphinx/examples/cpp/algorithms/vqe_h2.cpp) with IonQ simulator target,

nvq++ --emulate --target ionq cuda-quantum/docs/sphinx/examples/cpp/algorithms/vqe_h2.cpp
./a.out

it hit an assertion:

assert(static_cast<uint64_t>(index * element_size_bytes) < storage.size() &&
         "Provided index for Array::operator[] not valid.");

Seems like it is trying to access index 4 on a size-4 array.

Steps to reproduce the bug

nvq++ --emulate --target ionq cuda-quantum/docs/sphinx/examples/cpp/algorithms/vqe_h2.cpp
./a.out

Expected behavior

No assertion

Is this a regression? If it is, put the last known working version (or commit) here.

Not a regression

Environment

Suggestions

No response

1tnguyen commented 1 year ago

I've simplified the above example into a minimal reproducible example:

#include <cudaq.h>

__qpu__ void simple(cudaq::qubit &q, cudaq::qubit &r) {
  x(q);
  x(r);
}
__qpu__ void test() {
  const int n_qubits = 4;
  cudaq::qreg q(n_qubits);
  for (int i = 1; i + 2 < n_qubits; i += 2) {
    auto subq = q.slice(i, 2);
    simple(subq[0], subq[1]);
  }
  mz(q);
}

The issue seems to be in the loop unrolling pipeline:

cudaq-quake <file> | cudaq-opt --pass-pipeline='builtin.module(unrolling-pipeline)'

produced

module attributes {quake.mangled_name_map = {__nvqpp__mlirgen__function_simple._Z6simpleRN5cudaq5quditILm2EEES2_ = "_Z6simpleRN5cudaq5quditILm2EEES2_", __nvqpp__mlirgen__function_test._Z4testv = "_Z4testv"}} {
  func.func @__nvqpp__mlirgen__function_simple._Z6simpleRN5cudaq5quditILm2EEES2_(%arg0: !quake.ref, %arg1: !quake.ref) attributes {"cudaq-kernel"} {
    quake.x %arg0 : (!quake.ref) -> ()
    quake.x %arg1 : (!quake.ref) -> ()
    return
  }
  func.func @__nvqpp__mlirgen__function_test._Z4testv() attributes {"cudaq-entrypoint", "cudaq-kernel"} {
    %c4_i64 = arith.constant 4 : i64
    %c3_i64 = arith.constant 3 : i64
    %0 = quake.alloca !quake.veq<4>
    %1 = quake.subveq %0, %c3_i64, %c4_i64 : (!quake.veq<4>, i64, i64) -> !quake.veq<2>
    %2 = quake.extract_ref %1[0] : (!quake.veq<2>) -> !quake.ref
    %3 = quake.extract_ref %1[1] : (!quake.veq<2>) -> !quake.ref
    call @__nvqpp__mlirgen__function_simple._Z6simpleRN5cudaq5quditILm2EEES2_(%2, %3) : (!quake.ref, !quake.ref) -> ()
    %4 = quake.mz %0 : (!quake.veq<4>) -> !cc.stdvec<i1>
    return
  }
  func.func private @_Z4testv()
}

Line %1 = quake.subveq %0, %c3_i64, %c4_i64 : (!quake.veq<4>, i64, i64) -> !quake.veq<2> looks dubious as compared to the original code.

schweitzpgi commented 10 months ago

Works for me.