ProjectQ-Framework / ProjectQ

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

Too many non-reverted Computes causes a recursion error #344

Closed refi64 closed 3 years ago

refi64 commented 5 years ago

I found this to be really confusing:

from projectq.ops import Measure
import projectq
import projectq.meta

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

for i in range(1000):
    with projectq.meta.Compute(eng):
        pass

Measure | q0

yields:

Traceback (most recent call last):
  File "bell_state.py", line 12, 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
RecursionError: maximum recursion depth exceeded
 raised in:
'  File "/var/home/ryan/.virtualenvs/projectq/lib/python3.7/site-packages/projectq/meta/_compute.py", line 247, in receive'
'    self.send(command_list)'

The most bizarre part is that it errors on the Measure statement. I had commented out an Uncompute block for testing something but forgot to comment the compute, then it took me a few moments to figure out where the error was.

Takishima commented 5 years ago

The reason you get the recursion error stems from the way the context manager is implemented in the case of the Compute/Uncompute/CustomUncompute statements. This needs a bit of context to be fully understood.

The way a Compute context manager works in ProjectQ is that it inserts a ComputeEngine compiler engine right after the MainEngine in the engine list. This new engine basically records all the commands that pass through it so that these may be properly uncomputed in the future. Upon exiting the Compute region, this engine is not immediately dropped. Instead, the ComputeEngine gets dropped in the Uncompute/CustomUncompute region, which is not present in your case.

This essentially means that you have a list of 1000 ComputeEngine that are right after the MainEngine and each time a command gets sent by the MainEngine it calls the send method, which itself calls the receive method of CompilerEngine, which essentially lead to a recursion depth larger than what Python allows by default. This only happens when you send the Measure (or any other gate for that matter) because only then will the send and receive methods will get called.

I believe the main reason this was implemented so was to allow an easy way to check that upon reaching an Uncompute/CustomUncompute region, a corresponding Compute region exists.

refi64 commented 5 years ago

Could maybe there be a check that the size doesn't exceed some fixed amount, e.g. like 100 or so?

On Tue, Oct 29, 2019, 5:21 AM Nguyen Damien notifications@github.com wrote:

The reason you get the recursion error stems from the way the context manager is implemented in the case of the Compute/Uncompute/ CustomUncompute statements. This needs a bit of context to be fully understood.

The way a Compute context manager works in ProjectQ is that it inserts a ComputeEngine compiler engine right after the MainEngine in the engine list. This new engine basically records all the commands that pass through it so that these may be properly uncomputed in the future. Upon exiting the Compute region, this engine is not immediately dropped. Instead, the ComputeEngine gets dropped in the Uncompute/CustomUncompute region, which is not present in your case.

This essentially means that you have a list of 1000 ComputeEngine that are right after the MainEngine and each time a command gets sent by the MainEngine it calls the send method, which itself calls the receive method of CompilerEngine, which essentially lead to a recursion depth larger than what Python allows by default.

I believe the main reason this was implemented so was to allow an easy way to check that upon reaching an Uncompute/CustomUncompute region, a corresponding Compute region exists.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/ProjectQ-Framework/ProjectQ/issues/344?email_source=notifications&email_token=AAM4YSOGYASSTURHKJMAUY3QRAFBZA5CNFSM4JF66CG2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOECP62KQ#issuecomment-547351850, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAM4YSIWCWNXWYYAU6O4C3DQRAFBZANCNFSM4JF66CGQ .

Takishima commented 4 years ago

Sure that would be something fairly easy to implement. I don't know when I'll have the time to get to implementing it though.