pennylane icon indicating copy to clipboard operation
pennylane copied to clipboard

Capture measurements with `jax.make_jaxpr`.

Open albi3ro opened this issue 10 months ago • 2 comments

Context:

Description of the Change:

Benefits:

We can capture measurements and keep track of their resulting shapes in an extensible manner.

Possible Drawbacks:

Measurements do need a lot more hand-holding then observables due to the "duel mode" inputs of either wires or observables, and the need to fully specifying the resulting shape when we perform an actual measurment.

With the current framework, it will theoretically be extensible to add new measurements, but the process is slightly more error prone.

We also now provide duplicate information from MeasurementProcess.numeric_type, MeasurementProcess.shape, and MeasurementProcess._abstract_eval. But the instance-based property and method didn't play well with the need to track that information during an abstract evaluation.

Related GitHub Issues:

[sc-61200]

albi3ro avatar Apr 23 '24 19:04 albi3ro

Codecov Report

All modified and coverable lines are covered by tests :white_check_mark:

Project coverage is 99.67%. Comparing base (f5b805b) to head (08719a2). Report is 263 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #5564      +/-   ##
==========================================
- Coverage   99.67%   99.67%   -0.01%     
==========================================
  Files         416      416              
  Lines       38686    38549     -137     
==========================================
- Hits        38562    38424     -138     
- Misses        124      125       +1     

:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.

codecov[bot] avatar May 09 '24 22:05 codecov[bot]

One point that feels confusing is the qml.capture.measure during circuits. It correctly marks a quantum->classical transition, but it tells us that qml.capture.measure will not necessarily mark the endpoint of a circuit, which I thought it would 🤔

@dwierichs

We are eliminating the distinction between the "quantum part" and the "classical part", so terminal measurements don't necessarily need to be the last thing in the plxpr.

For example, here we retrieve samples, but then we postprocess them classically.

def f():
    mp = qml.sample(wires=(0,1))
    samples = qml.capture.measure(mp, shots=50)
    return jax.numpy.sum(*samples,axis=0)/50

jax.make_jaxpr(f)()
{ lambda ; . let
    a:AbstractMeasurement(n_wires=2) = sample_wires 0 1
    b:i32[50,2] = measure[num_device_wires=0 shots=Shots(total=50)] a
    c:i32[2] = reduce_sum[axes=(0,)] b
    d:f32[2] = convert_element_type[new_dtype=float32 weak_type=False] c
    e:f32[2] = div d 50.0
  in (e,) }

While this type of stuff won't necessarily be captured from the user in the current QNode, it dramatically increases what can do internally with transformations and compilation.

Maybe this wont' be the way we go long term, but I think it will be rather useful to be able to include classical postprocessing inside the same program.

albi3ro avatar May 23 '24 14:05 albi3ro

Ah, yes, I should have been more clear with what I mean by "end of a circuit". I meant that we can not infer that a quantum circuit has been "finished", in the sense of deallocating/erasing the qubit register state, whether it is during a captured program or at its end. I was thinking about quantum gradients that need to dynamically find the scope of a quantum circuit and apply, say, the parameter-shift rule to the quantum parts of a hybrid program when differentiating it.

dwierichs avatar May 23 '24 14:05 dwierichs

Note that this is something that we discussed with Romain and Mikhail last quarter during the discovery --- that is, to allow developers and user to easily write transforms when we have the new hybrid program capture, we need a way to (temporarily) 'separate' the hybrid program into classical and quantum 'blocks', so that transforms only apply to the quantum components.

This will be a huge quality of life improvement; in Catalyst currently, any quantum transformation in MLIR has to manually do this separation, apply the transform to the quantum part, and then glue the hybrid program back together.

@trbromley and I are thinking about having a discovery for transforms + PLXPR in Q3

josh146 avatar May 23 '24 17:05 josh146

in Catalyst currently, any quantum transformation in MLIR has to manually do this separation, apply the transform to the quantum part, and then glue the hybrid program back together

I don't think this is accurate for all transforms. The quantum and classical instructions live in the same scope, but you are free to traverse only operations from the quantum dialect, modify them, delete them, insert new ones, etc. This makes it particularly easy to write peephole optimizations on the quantum circuit. However for certain transformations, like differentiation, where you need to treat the classical scope and quantum scope fundamentally differently, and also transformations where you restructure or copy regions of quantum instructions, because you will also need to be aware of the classical values they depend on and where those come from.

One point that feels confusing is the qml.capture.measure during circuits. It correctly marks a quantum->classical transition, but it tells us that qml.capture.measure will not necessarily mark the endpoint of a circuit, which I thought it would 🤔

We are eliminating the distinction between the "quantum part" and the "classical part", so terminal measurements don't necessarily need to be the last thing in the plxpr. For example, here we retrieve samples, but then we postprocess them classically. I think it will be rather useful to be able to include classical postprocessing inside the same program.

Ah, yes, I should have been more clear with what I mean by "end of a circuit". I meant that we can not infer that a quantum circuit has been "finished", in the sense of deallocating/erasing the qubit register state, whether it is during a captured program or at its end. I was thinking about quantum gradients that need to dynamically find the scope of a quantum circuit and apply, say, the parameter-shift rule to the quantum parts of a hybrid program when differentiating it.

@albi3ro is there only one qml.capture.measure operation, or can there be multiple in different points of the program? I agree with David that it is somewhat important to have a well defined scope for where the qubits are "live", and to define the scope of what an instruction like qml.expval should act on. A situation like the following would leave a lot of ambiguity I think:

{
  # some gates
  qml.capture.measure(qml.expval)
  # some more gates
  qml.capture.measure(qml.probs)
}

What is the scope of the expectation value, some gates or some gates + some more gates? What about probs, is just some more gates or some gates + some more gates? Do the two measures imply two different circuits? Could you use the qml.expval value as argument to some more gates? Note that we did experiment in Catalyst with allowing measurement processes to appear anywhere in a quantum function, because on simulators those can just be computed on the current internal state without affecting anything. On hardware, since the measurement processes imply (repeated) execution of a complete quantum circuit, what that circuit is has to be well defined. I think this is one of the reasons other frameworks define measurement processes as acting on a "quantum kernel" or "quantum circuit", rather than being part of it.

I do agree that including post-processing in the program should definitely be a goal, although I'm not sure if it has to be in the same scope. The qnode primitive you are working on could delineate that scope for example, but it could also be something else.

Differentiation is definitely made easier when everything is separated into pre-processing, quantum, post-processing, but I don't think that structure can cover some of the more hybrid/dynamic constructs we are targeting anyways.

dime10 avatar May 23 '24 18:05 dime10

I don't think this is accurate for all transforms. The quantum and classical instructions live in the same scope, but you are free to traverse only operations from the quantum dialect, modify them, delete them, insert new ones, etc. This makes it particularly easy to write peephole optimizations on the quantum circuit. However for certain transformations, like differentiation, where you need to treat the classical scope and quantum scope fundamentally differently, and also transformations where you restructure or copy regions of quantum instructions, because you will also need to be aware of the classical values they depend on and where those come from.

@dime10 ah good to know! I was thinking of the error mitigation and gradient transforms when chatting to Romain --- these were examples where he brought up this separation last quarter.

josh146 avatar May 23 '24 19:05 josh146

Should the test file be renamed to tests/capture/test_measurements.py?

dwierichs avatar May 29 '24 09:05 dwierichs

Looks good!

I didn't see anything about the "capture.measure" (not the mcm) operation in the code, has been shelved for now?

@dime10 Decided to break the capture.measure quantum-classical boundary into a later step to simplify the review process on this first component. Been shelved for now, and we can come back to some version of it when we need to work with mid circuit measurements better.

albi3ro avatar May 29 '24 19:05 albi3ro

Looks good! I didn't see anything about the "capture.measure" (not the mcm) operation in the code, has been shelved for now?

@dime10 Decided to break the capture.measure quantum-classical boundary into a later step to simplify the review process on this first component. Been shelved for now, and we can come back to some version of it when we need to work with mid circuit measurements better.

Sounds good to me! But isn't this role filled by the qnode primitive now, that is the primitive both delineates the quantum scope as well turns abstract measurement return values into numeric data?

dime10 avatar May 29 '24 20:05 dime10

Looks good! I didn't see anything about the "capture.measure" (not the mcm) operation in the code, has been shelved for now?

@dime10 Decided to break the capture.measure quantum-classical boundary into a later step to simplify the review process on this first component. Been shelved for now, and we can come back to some version of it when we need to work with mid circuit measurements better.

Sounds good to me! But isn't this role filled by the qnode primitive now, that is the primitive both delineates the quantum scope as well turns abstract measurement return values into numeric data?

We'll need something like it anyway for mid circuit measurements. As for allowing it to be called on other measurements, we can probably wait to add that support till we actually need it and have a better idea of our constraints.

albi3ro avatar May 29 '24 20:05 albi3ro