ProjectQ-Framework / ProjectQ

ProjectQ: An open source software framework for quantum computing
https://projectq.ch
Apache License 2.0
876 stars 271 forks source link

Bizarre IndexError if attempt to measure a qubit after a Control block #343

Open refi64 opened 4 years ago

refi64 commented 4 years ago

Repro:

from projectq.ops import X, Measure
import projectq
import projectq.meta

eng = projectq.MainEngine()
q0 = eng.allocate_qubit()

with projectq.meta.Control(eng, q0):
    X | q0

# eng.flush()
Measure | q0
print(int(q0))

This yields:

Traceback (most recent call last):
  File "bell_state.py", line 18, in <module>
    Measure | q0
  File "/var/home/ryan/.virtualenvs/projectq/lib/python3.7/site-packages/projectq/ops/_gates.py", line 284, in __or__
    apply_command(cmd)
  File "/var/home/ryan/.virtualenvs/projectq/lib/python3.7/site-packages/projectq/ops/_command.py", line 58, in apply_command
    engine.receive([cmd])
  File "/var/home/ryan/.virtualenvs/projectq/lib/python3.7/site-packages/projectq/cengines/_main.py", line 266, in receive
    self.send(command_list)
  File "/var/home/ryan/.virtualenvs/projectq/lib/python3.7/site-packages/projectq/cengines/_main.py", line 288, in send
    raise compact_exception  # use verbose=True for more info
IndexError: list index out of range
 raised in:
'  File "/var/home/ryan/.virtualenvs/projectq/lib/python3.7/site-packages/projectq/cengines/_optimize.py", line 136, in _optimize'
'    if inv == self._l[idx][i + 1]:'

With verbose=True:

Traceback (most recent call last):
  File "bell_state.py", line 18, in <module>
    Measure | q0
  File "/var/home/ryan/.virtualenvs/projectq/lib/python3.7/site-packages/projectq/ops/_gates.py", line 284, in __or__
    apply_command(cmd)
  File "/var/home/ryan/.virtualenvs/projectq/lib/python3.7/site-packages/projectq/ops/_command.py", line 58, in apply_command
    engine.receive([cmd])
  File "/var/home/ryan/.virtualenvs/projectq/lib/python3.7/site-packages/projectq/cengines/_main.py", line 266, in receive
    self.send(command_list)
  File "/var/home/ryan/.virtualenvs/projectq/lib/python3.7/site-packages/projectq/cengines/_main.py", line 275, in send
    self.next_engine.receive(command_list)
  File "/var/home/ryan/.virtualenvs/projectq/lib/python3.7/site-packages/projectq/cengines/_tagremover.py", line 58, in receive
    self.send([cmd])
  File "/var/home/ryan/.virtualenvs/projectq/lib/python3.7/site-packages/projectq/cengines/_basics.py", line 185, in send
    self.next_engine.receive(command_list)
  File "/var/home/ryan/.virtualenvs/projectq/lib/python3.7/site-packages/projectq/cengines/_optimize.py", line 244, in receive
    self._cache_cmd(cmd)
  File "/var/home/ryan/.virtualenvs/projectq/lib/python3.7/site-packages/projectq/cengines/_optimize.py", line 224, in _cache_cmd
    self._check_and_send()
  File "/var/home/ryan/.virtualenvs/projectq/lib/python3.7/site-packages/projectq/cengines/_optimize.py", line 196, in _check_and_send
    self._optimize(i)
  File "/var/home/ryan/.virtualenvs/projectq/lib/python3.7/site-packages/projectq/cengines/_optimize.py", line 136, in _optimize
    if inv == self._l[idx][i + 1]:
IndexError: list index out of range

Of course, eng.flush() fixes the issue, but this should probably raise that relevant exception instead of the more obscure IndexError...

On that note, is there a reason why this doesn't work:

from projectq.ops import X, Measure
import projectq
import projectq.meta

eng = projectq.MainEngine()
q0 = eng.allocate_qubit()

X | q0
eng.flush()
Measure | q0
assert int(q0) == 1  # okay
# should flip q0 back to 0, since it being 1 should enter the control block
with projectq.meta.Control(eng, q0):
    X | q0

eng.flush()
Measure | q0
assert int(q0) == 0  # fail

EDIT: I just realized my eng.flush()'s were before the Measure statements in order to avoid the crash, but aren't they supposed to be after?

EDIT 2: The same crash can be reproduced via CNOT | (q0, q0).

Takishima commented 4 years ago

eng.flush() statements need to be put before accessing the result of some measurements.

Now, I am not sure having a CNOT gate with identical control and target qubits makes a lot of sense, because by definition, the control target should not be modified after the application of the gate. Also, it is usually understood that 2-qubit gate get applied on two different qubits.

It is true, however, that ProjectQ currently does not checks whether control and target qubits are disjoints.

refi64 commented 4 years ago

Yeah identical qubits in a CNOT isn't useful or even allowed by most frameworks, but crashing with an obscure IndexError isn't very helpful in that case either...

Takishima commented 4 years ago

Yeah identical qubits in a CNOT isn't useful or even allowed by most frameworks, but crashing with an obscure IndexError isn't very helpful in that case either...

I agree! So I'll keep this issue open until we fix this (hopefully sometime soon)