Closed doug-q closed 3 days ago
Further thoughts:
I have implemented the minimum pass in the linked PR. This replaces measure
with lazy_measure
but instead of delaying the read
for as long as possible, does it immediately. I think this is enough to move forward with codegen.
I think we should rename Lazy -> Future. We should remove the Lift
operation.
In the lazify-measure
pass, for now, we will replace values of type BOOL_T
returned from quantum.tket2.measure
with values of the sum type BOOL_T + Future<BOOL_T>
. When the value is scrutinised (for example the control wire of a Conditional
), we will insert a new Conditional
node that either forwards the first variant or tket2.future.read
s the second variant.
In the future we can introduce a Lazy<T>
type, which has a lift
(and map
etc) operation, and which is lowered to T + Future<T>
.
The need for dup
here is critical. If we didn't need to dup
then we could take Lazy<BOOL_T>
as QUBIT
and read
thereof as measure
....
if (given) we do need dup
then these Lazy
s have basically (exactly??) the same interface as Ptr
s i.e. https://github.com/CQCL-DEV/hugrverse/issues/21 right?
The need for
dup
here is critical. If we didn't need todup
then we could takeLazy<BOOL_T>
asQUBIT
andread
thereof asmeasure
....if (given) we do need
dup
then theseLazy
s have basically (exactly??) the same interface asPtr
s i.e. CQCL-DEV/hugrverse#21 right?
They will lower to reference counted resources, so they are much the same as Ptr
s. They replace bools, where out ports may have many uses. We do not necessarily have to make Lazy
linear, in the same way we do not necessarily have to make Ptr
s linear. If they are linear, then we need dup
and free
.
They are not exactly the same as Ptr
s, they have no write
op.
They are not exactly the same as
Ptr
s, they have nowrite
op.
Ah, yes, true. The writing is captured in the closure of how to compute the value.
And (importantly) this closure, even tho referred to by the non-linear/dup
-able Lazy, can capture the linear QUBIT because it can only be called once.
Agreed Lazy/Future is a useful general thing to have. Is it any easier if we do something Qubit/measure-specific? I'm not sure it is (==> this is "develop Future
extension", dependent on the Ptr
extension - I think this Future effectively wraps some version of Ptr<Sum<QUBIT | Bool>>
). Methinks the "push evaluation of lazy things downwards" applies generally and not just to Qubits.
I note also that there is a counterargument that we should do measurement early, to free up qubits for dynamic allocation. The usual instruction-scheduling/register-allocation phase-order problem, one might say ;-) :-(
They are not exactly the same as
Ptr
s, they have nowrite
op.Ah, yes, true. The writing is captured in the closure of how to compute the value.
And (importantly) this closure, even tho referred to by the non-linear/
dup
-able Lazy, can capture the linear QUBIT because it can only be called once.
To be clear, nothing here constructs a Future<QB_T>
, the only future type is Future<BOOL_T>
, of course the extension does allow linear type args to Future
.
We could disallow linear type args to Future
if we decideFuture
itself is not linear, or we could have the bound on Future
depend on the bound of the type arg.
Agreed Lazy/Future is a useful general thing to have. Is it any easier if we do something Qubit/measure-specific? I'm not sure it is (==> this is "develop
Future
extension", dependent on thePtr
extension - I think this Future effectively wraps some version ofPtr<Sum<QUBIT | Bool>>
).
This Future
extension is not dependent on Ptr
at all. They lower to different runtime calls. and Future
will be represented as some integral index rather than a pointer.
Methinks the "push evaluation of lazy things downwards" applies generally and not just to Qubits.
This is why I have separated the futures
and tket2.quantum.lazy
extensions.
I note also that there is a counterargument that we should do measurement early, to free up qubits for dynamic allocation. The usual instruction-scheduling/register-allocation phase-order problem, one might say ;-) :-(
Please do see Lazy Measurements
on confluence to see how I see this api evolving, in particular a wait_on_multiple_futures
op.
I have dreams of introducing a Lazy
extension which uses Future
to offer a Monadic interface. This is not something one would want for a Ptr
.
One can imagine we would would have different kinds of futures(in the future lol), representing different future-like interfaces offered by the runtime. In this world we would identify different kinds of futures by another type param.
The h-series runtime wants to be lazy in using the results of measurements, to facilitate runtime pipelining.
I suggest we can achieve this with the following:
lazy
extension withLazy<T>
lift<T>: T -> Lazy<T>
read<T>: Lazy<T> -> T
measure: Qubit -> (Qubit, Lazy<BOOL_T>)
BOOL_T
result of "quantum.tket2.measure":Conditional
control input, output-of-DataflowBlock
control input, output-of-TailLoop
control input, indirect call, call to public function, or custom op input:read
the LazyLazy<BOOL_T>
and iterate Note that here a public function is either aFuncDecl
or a function which is the target ofLoadFunction
I intend to start with the
Lazy<T>
type being linear, and addingdup
andfree
functions. I anticipate that lowerings will want to manage waitable "handles".dup
will lower to "increment reference count" andfree
will lower to "decrement reference count". If making it linear ends up being too hard I will switch to non-linear to get something working.This code should live in
tket2-hseries