Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RFC] Mid-circuit measurement and sampling in library mode #169

Open
amccaskey opened this issue May 17, 2023 · 0 comments
Open

[RFC] Mid-circuit measurement and sampling in library mode #169

amccaskey opened this issue May 17, 2023 · 0 comments
Assignees
Labels
RFC Request for Comments

Comments

@amccaskey
Copy link
Collaborator

Background

Now that we are moving simulation based targets to runtime-only library mode (no MLIR compilation), we need to revisit how we enable sampling of dynamic circuits (those circuits with mid-circuit measurement + conditional feedback). An example circuit for this is

struct kernel {
  void operator()() __qpu__ {
    cudaq::qreg<3> q;
    // Initial state preparation
    x(q[0]);

    // Create Bell pair
    h(q[1]);
    x<cudaq::ctrl>(q[1], q[2]);

    x<cudaq::ctrl>(q[0], q[1]);
    h(q[0]);

    auto b0 = mz(q[0]);
    auto b1 = mz(q[1]);

    if (b1)
      x(q[2]);
    if (b0)
      z(q[2]);

    mz(q[2]);
  }
};

For a standard kernel with no measurements or measurements appended at the end of the function, backend simulations can simulate the circuit a single time and sample the final state, thereby producing the histogram of bit strings and counts. In the presence of non-trivial control flow with conditional statements like above, we cannot do that, and instead must invoke the circuit numShots times, collecting measured bit strings each time. The question becomes, in a purely runtime model, how does one indicate or know that a CUDA Quantum kernel has conditional statements on measurement results, and thus switch to this second model of sampling (invoking the kernel numShots times)? In the MLIR compilation mode, this is straightforward because we have the MLIR representation of the kernel at runtime and can look and see if it has cc.if() operations on values coming from a quake.mz() operation.

Potential solutions

Tracing the kernel

The first solution here is to borrow from the tracer PR (#92) with the addition of a defined type for measurement results. Imagine a measure_result type with the following structure

class measure_result {
private:
  bool result = false;

public:
  measure_result(const bool &b) : result(b) {};
  operator bool();
};

One could implement the implicit bool conversion operator on a type like this to trip some sort of flag indicating that the current kernel execution has conditional statements on measurement results (if (b0) invokes measure_result::operator bool()). With this in place, cudaq::sample(...) could be updated to first trace the function (no execution), and pick up any flag that was tripped by an implicit operator bool conversion on a measurement result.

This approach is nice because it requires no change to the language specification or the structure of kernel expressions / cudaq::sample(). I have a prototype for this here.

Kernel function indicator

Another approach could rely on some sort of helper function + user input on when a kernel has a mid-circuit measurement + conditional statement. Something like

struct kernel {
  void operator()() __qpu__ {
    cudaq::qreg<3> q;

    // For simulation / library mode, programmers 
    // have to indicate this is a dynamic circuit, 
    // and provide the names of any classical registers 
    // we'll create
    cudaq::is_dynamic_kernel("b0", "b1");

    // Initial state preparation
    x(q[0]);

    // Create Bell pair
    h(q[1]);
    x<cudaq::ctrl>(q[1], q[2]);

    x<cudaq::ctrl>(q[0], q[1]);
    h(q[0]);

    auto b0 = mz(q[0]);
    auto b1 = mz(q[1]);

    if (b1)
      x(q[2]);
    if (b0)
      z(q[2]);

    mz(q[2]);
  }
};

... sampling invoked via 
auto counts = cudaq::sample({nShots, /* is dynamic circuit */ true}, kernel{});

This is not as preferable to me, in that we have a different sample signature for physical vs simulated backends. I'd like those to stay the same. Moreover, it makes kernels targeting physical vs simulated backends different, with the addition of some kind of indicator function.

@bettinaheim bettinaheim added the RFC Request for Comments label Apr 5, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
RFC Request for Comments
Projects
None yet
Development

No branches or pull requests

3 participants