BQSKit / bqskit

Berkeley Quantum Synthesis Toolkit
Other
113 stars 32 forks source link

“Lazy Map” capability for the BQSKit runtime #260

Open WolfLink opened 1 month ago

WolfLink commented 1 month ago

I’d like the ability to perform a “lazy map”, meaning to have a RuntimeFuture to which I can submit more tasks. Here’s an example scenario where this would be useful:

  1. I start an asynchronous task A
  2. I perform some other code
  3. I start an asynchronous task B
  4. I wait for a result from either A or B (whichever finishes first)
  5. I cancel the other task (or all such other tasks if you expand this pattern beyond 2 tasks).

I imagine the code looking something like this:

lazy_map = get_runtime().lazy_map()
lazy_map.submit(A)
# other code
lazy_map.submit(B)
result = await lazy_map
lazy_map.cancel()

Another way to do this would be a way to await a list of futures

edyounis commented 1 month ago

We do have the get_runtime().next(), but the functionality is tied to a map future.

Is what you are asking for here to be able to add additional slots to an existing in-flight task? That is, do you expect A to start running, or be ready to, as soon as you submit it?

If so, we could probably create a "submit_additional" or "map_additional" method that takes a future and a task and appends the task to the existing future while submitting it to the runtime rather than creating a new future. We would have to just be sure that the mailbox attached to the future is valid and alive.

WolfLink commented 1 month ago

Yeah that's more or less the idea I am going for. For context, the specific code I have that inspired this idea is similar to:

A_future = get_runtime.map(...)
B_futures = []
async for result in FutureIterate(A_future):
    if condition(result):
        B_futures.append(get_runtime.submit(...))
    if other_condition(result):
        get_runtime().cancel(A_future)
        break

B_results = [await B_future for B_future in B_futures]

This works fine for now because I do want to look at all the results from B_futures, but I might at some point instead just want to get the "first good result" from B_futures. I would want some code like:

async for result in FutureIterate(B_futures):
    if condition(result):
        get_runtime().cancel(B_futures)
        return result
return None

This is currently not possible to implement with the existing BQSKit runtime features.

Tangentially, FutureIterate is a tool I wrote that wraps calls to get_runtime().next(map_future) in an async iterator such that it can be used in an async for loop. This might be a tool that would be good to have in BQSKit.

WolfLink commented 1 month ago

I have encountered a real-world use case where I would like this feature.