Accessing "old" measured parameters

Suppose you wish to build a program which does feed forward of measurement results but also wish to minimise the number of used qumodes to reduce memory cost. A minimal example would be like the following:

prog = sf.Program(1)
with prog.context as q:
   ops.Coherent(0, 0)        | q[0]
   ops.MeasureX              | q[0]  #1
   ops.Coherent(q[0].par, 0) | q[0]
   ops.MeasureX              | q[0]  #2
   ops.Coherent(?, 0)        | q[0]  #3

Here qumode 0 is reused after it has been measured the first time at #1. The new state at qumode 0 is then again measured at #2. In the documentation it is stated that

The latest measurement result of qumode i is available via q[i].par.

However, there are no references about how to access measurement results older than the latest. Say you wish to prepare at #3 a coherent state dependent on both the result at #1 as well as that at #2. Is there a way supported by Blackbird or otherwise to do this. Something like ops.Coherent(q[0#1].par + q[0#2].par, 0) or maybe like ops.Coherent(prog.samples[0] + prog.samples[1], 0).

Hey @fkm! Welcome to the forum :rocket:

Objects like, in your code example, q[0].par are a measurement’s symbolic value. In other words — and this is in the documentation for MeasuredParameter — it’s used for deferred measurements, i.e., using a measurement’s value symbolically in defining a gate before the numeric value of that measurement is available.

What you can do is use sf.parameters.par_evaluate(q). Here is a dummy example:

import strawberryfields as sf
from strawberryfields.ops import *

prog = sf.Program(2)

with prog.context as q:
    MeasureFock() | q[0]
    Dgate(q[0].par ** 2) | q[1]
    MeasureFock() | q[1]

eng = sf.Engine("fock", backend_options={"cutoff_dim": 6})
results = eng.run(prog)

for outcome in sf.parameters.par_evaluate(q):
    print(outcome.ind, outcome.val)

'''
0 [0]
1 [0]
'''

sf.parameters.par_evaluate will create a list whose length is the number of times you perform a measurement inside of a circuit (in the above example, there are two measurements). ind is the number associated to when the measurement happened in relation to all of the other ones, and val is the measurement outcome. Let me know if that helps!

Thank you. I am looking forward to engaging in the dsicussions.

Your answer correctly supply the values of all measurements performed on q[0] by evaluating the symbolic reference. This is possible only after they are determined by eng.run(prog). However, I need these values for feed-forward at the time of defining the program within the with prog.context as q: block. This is indeed before they are determined by eng.run(prog), and so, it is exactly the symbolic reference to all previous measurements of q[0] that I need. However, through q[0].par is seems like only the latest/most recent measurement is available and not earlier ones.

Explaining my example: In place of the ? at #3 (bad choice of notation on my part) I need a symbolic reference to both the latest measurement of q[0] performed at #2 available through q[0].par, and also to the measurement performed earlier at #1 which I do not know how to access.

Oh I see! Does something like this work?

with prog.context as q:
    Dgate(0.123) | q[0]
    MeasureFock() | q[0]
    placeholder = q[0].par # use this value later on as well
    Dgate(q[0].par ** 2) | q[0]
    MeasureFock() | q[0]
    Dgate(placeholder ** 2) | q[0]

Unfortunately something like this does not work. The placeholder variable becomes simply a symbolic reference to the qmode q[0] which is evaluated only when asked for in a symbolic expression. Here is a slightly modified version of your example with numbering of select lines.

prog = sf.Program(1)
with prog.context as q:
    ops.Fock(0) | q[0]
    ops.MeasureFock() | q[0]      #1
    placeholder = q[0].par        #2
    
    ops.Fock(2) | q[0]
    ops.MeasureFock() | q[0]      #3
    
    ops.Fock(placeholder) | q[0]  #4

eng = sf.Engine("fock", backend_options={"cutoff_dim": 3})
result = eng.run(prog)
print(result.state.data)

'''
[0.+0.j 0.+0.j 1.+0.j]
'''

In the example, the value of placeholder at line 4 is not the outcome of the first measurement performed at line 1 (which is 0) but rather of that performed in line 3 (which is 2).

This is due to the fact that when running line 4, the symbolic reference is evaluated and the reference placeholder = q[0].par is replaced by its current value, which is not in general the same as the value of q[0].par at the time of defining placeholder in line 1.

Ah, okay I see. I don’t think this is within the scope of possibilities with Strawberryfields :confused:.