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

Adjusting UI for measurement on several modes #403

Open
antalszava opened this issue Jun 4, 2020 · 3 comments
Open

Adjusting UI for measurement on several modes #403

antalszava opened this issue Jun 4, 2020 · 3 comments

Comments

@antalszava
Copy link
Contributor

Issue description

Strawberry Fields allows having multi-mode measurements in a Program by having measurement operators targetting several registers that reference the modes to be measured.

Within the definition of a program, this means having MeasurementOperator | (q[0], q[1], q[2], ...) , where q[0], q[1], ... refer to the registers of the modes to be measured.

Intuitively, one could want to define the same measurement by having consecutive measurement operators targetting a single register in the following fashion:

    MeasurementOperator | q[0]
    MeasurementOperator | q[1]
    MeasurementOperator | q[2]
    (...)

Specific example
Let's have a look at a specific example, where it is expected to see correlation between the outcomes for each mode

  1. Running the following example:
prog = sf.Program(2)
eng = sf.Engine("gaussian")
with prog.context as q:
    S2gate(1) | (q[0], q[1])
    MeasureFock() | (q[0], q[1])
results = eng.run(prog, shots=5)
results.samples

can result in an output like:

array([[1, 1],
       [4, 4],
       [1, 1],
       [0, 0],
       [1, 1]])
  1. This is in contrast with the following Program definition:
prog = sf.Program(2)
eng = sf.Engine("gaussian")
with prog.context as q:
    S2gate(1) | (q[0], q[1])
    MeasureFock() | q[0]
    MeasureFock() | q[1]
results = eng.run(prog, shots=5)
results.samples

which can result in an output like:

array([[1, 1],
       [0, 1],
       [1, 2],
       [3, 0],
       [2, 4]])

With the second example, mode q[0] gets reinitialized to the vacuum state after being measured.

  • Expected behavior:
    The previous two code snippets both output samples where the samples for each run of the program are correlated.

  • Actual behavior:
    There is a correlation between samples of the two modes for each run of the program only for the first code example.

  • Reproduces how often:
    Each time.

  • System information:

Strawberry Fields: a Python library for continuous-variable quantum circuits.
Copyright 2018-2020 Xanadu Quantum Technologies Inc.

Python version:            3.7.6
Platform info:             Linux-4.19.11-041911-generic-x86_64-with-debian-buster-sid
Installation path:         /anaconda3/lib/python3.7/site-packages/strawberryfields
Strawberry Fields version: 0.14.0
Numpy version:             1.18.4
Scipy version:             1.4.1
SymPy version:             1.5.1
NetworkX version:          2.3
The Walrus version:        0.12.0
Blackbird version:         0.2.3
TensorFlow version:        2.0.0

Additional information

This would most likely include a change in how compilers handle measurement operators and introduce logic so that compilers will automatically group measurements that are terminal.

@josh146
Copy link
Member

josh146 commented Jun 5, 2020

Also worth mentioning that while this is unintuitive, it is also expected behaviour; the backend API was designed such that after a measurement operation, the mode is always traced out and replaced with the vacuum state. This allows the state to be re-used without needing to add additional modes to the system, which might be costly.

I propose that we simply modify the compilers such that terminal measurements of a program (e.g, those that are 'dangling' nodes of the DAG with no other operations depending on them) are compiled into a single measurement result.

That is,

Sgate(0.5) | q[0]
MeasureX  | q[0]
S2gate(q[0].par) | (q[0], q[1])
MeasureFock() | q[0]
Xgate(0.5) | q[2]
MeasureFock() | q[1]
MeasureX | q[2]

would be compiled to give

Sgate(0.5) | q[0]
MeasureX  | q[0]
S2gate(q[0].par) | (q[0], q[1])
Xgate(0.5) | q[2]
MeasureFock() | (q[0], q[1])
MeasureX | q[2]

where the two terminal fock measurements are combined, but the non-terminal MeasureX in the middle of the circuit is not.

@felipeoyarce
Copy link
Contributor

Hi @antalszava and @josh146 ! I would like to work on this issue 🤗

@josh146
Copy link
Member

josh146 commented Apr 19, 2021

Hi @felipeoyarce, nice! Feel free to open a WIP PR, and ask any questions as they arise :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants