Skip to content

Commit

Permalink
Add insert_engine/drop_engine_after meta utility methods (#115)
Browse files Browse the repository at this point in the history
  • Loading branch information
Strilanc authored and damiansteiger committed Jun 15, 2017
1 parent 74e593c commit 22259a1
Show file tree
Hide file tree
Showing 7 changed files with 125 additions and 51 deletions.
1 change: 1 addition & 0 deletions projectq/meta/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,4 @@
from ._control import (Control,
get_control_count)
from ._dagger import Dagger
from ._util import insert_engine, drop_engine_after
40 changes: 16 additions & 24 deletions projectq/meta/_compute.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@
of the entire operation on the value of a qubit / register (only Action needs
controls). This file also defines the corresponding meta tags.
"""

from copy import deepcopy

import projectq
from projectq.cengines import BasicEngine
from projectq.ops import Allocate, Deallocate
from ._util import insert_engine, drop_engine_after


class QubitManagementError(Exception):
Expand Down Expand Up @@ -145,17 +147,16 @@ def run_uncompute(self):
if cmd.gate == Deallocate:
assert (cmd.qubits[0][0].id) in ids_local_to_compute
# Create new local qubit which lives within uncompute section
# Allocate needs to have old tags + uncompute tag
oldnext = self.next_engine

# Allocate needs to have old tags + uncompute tag
def add_uncompute(command, old_tags=deepcopy(cmd.tags)):
command.tags = old_tags + [UncomputeTag()]
return command
self.next_engine = projectq.cengines.CommandModifier(
add_uncompute)
self.next_engine.next_engine = oldnext
tagger_eng = projectq.cengines.CommandModifier(add_uncompute)
insert_engine(self, tagger_eng)
new_local_qb = self.allocate_qubit()
self.next_engine = oldnext
drop_engine_after(self)

new_local_id[cmd.qubits[0][0].id] = deepcopy(
new_local_qb[0].id)
# Set id of new_local_qb to -1 such that it doesn't send a
Expand Down Expand Up @@ -351,18 +352,16 @@ def __init__(self, engine):
commands (normally: MainEngine).
"""
self.engine = engine
self._compute_eng = None

def __enter__(self):
compute_eng = ComputeEngine()
compute_eng.main_engine = self.engine.main_engine
oldnext = self.engine.next_engine
self.engine.next_engine = compute_eng
compute_eng.next_engine = oldnext
self._compute_eng = compute_eng
self._compute_eng = ComputeEngine()
insert_engine(self.engine, self._compute_eng)

def __exit__(self, type, value, traceback):
# notify ComputeEngine that the compute section is done
self._compute_eng.end_compute()
self._compute_eng = None


class CustomUncompute(object):
Expand Down Expand Up @@ -407,16 +406,11 @@ def __enter__(self):
# after __enter__
self._allocated_qubit_ids = compute_eng._allocated_qubit_ids.copy()
self._deallocated_qubit_ids = compute_eng._deallocated_qubit_ids.copy()
oldnext = compute_eng.next_engine
self.engine.next_engine = oldnext
drop_engine_after(self.engine)

# Now add uncompute engine
uncompute_eng = UncomputeEngine()
uncompute_eng.main_engine = self.engine.main_engine
oldnext = self.engine.next_engine
self.engine.next_engine = uncompute_eng
uncompute_eng.next_engine = oldnext
self._uncompute_eng = uncompute_eng
self._uncompute_eng = UncomputeEngine()
insert_engine(self.engine, self._uncompute_eng)

def __exit__(self, type, value, traceback):
# Check that all qubits allocated within Compute or within
Expand All @@ -431,8 +425,7 @@ def __exit__(self, type, value, traceback):
"been allocated in the with Compute(eng) or with " +
"CustomUncompute(eng) context.")
# remove uncompute engine
oldnext = self._uncompute_eng.next_engine
self.engine.next_engine = oldnext
drop_engine_after(self.engine)


def Uncompute(engine):
Expand All @@ -453,5 +446,4 @@ def Uncompute(engine):
"corresponding 'with Compute' statement "
"found.")
compute_eng.run_uncompute()
oldnext = compute_eng.next_engine
engine.next_engine = oldnext
drop_engine_after(engine)
12 changes: 4 additions & 8 deletions projectq/meta/_control.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@
"""

from projectq.cengines import BasicEngine
from projectq.ops import ClassicalInstructionGate
from projectq.meta import ComputeTag, UncomputeTag
from projectq.ops import ClassicalInstructionGate
from projectq.types import BasicQubit
from ._util import insert_engine, drop_engine_after


class ControlEngine(BasicEngine):
Expand Down Expand Up @@ -101,17 +102,12 @@ def __init__(self, engine, qubits):
def __enter__(self):
if len(self._qubits) > 0:
ce = ControlEngine(self._qubits)
ce.main_engine = self.engine.main_engine
oldnext = self.engine.next_engine
self.engine.next_engine = ce
ce.next_engine = oldnext
self._ce = ce
insert_engine(self.engine, ce)

def __exit__(self, type, value, traceback):
# remove control handler from engine list (i.e. skip it)
if len(self._qubits) > 0:
oldnext = self._ce.next_engine
self.engine.next_engine = oldnext
drop_engine_after(self.engine)


def get_control_count(cmd):
Expand Down
15 changes: 6 additions & 9 deletions projectq/meta/_dagger.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

from projectq.cengines import BasicEngine
from projectq.ops import Allocate, Deallocate
from projectq.meta import DirtyQubitTag
from ._util import insert_engine, drop_engine_after


class QubitManagementError(Exception):
Expand Down Expand Up @@ -123,18 +123,15 @@ def __init__(self, engine):
QFT | qubits
"""
self.engine = engine
self._dagger_eng = None

def __enter__(self):
dagger_eng = DaggerEngine()
dagger_eng.main_engine = self.engine.main_engine
oldnext = self.engine.next_engine
self.engine.next_engine = dagger_eng
dagger_eng.next_engine = oldnext
self._dagger_eng = dagger_eng
self._dagger_eng = DaggerEngine()
insert_engine(self.engine, self._dagger_eng)

def __exit__(self, type, value, traceback):
# run dagger engine
self._dagger_eng.run()
self._dagger_eng = None
# remove dagger handler from engine list (i.e. skip it)
oldnext = self._dagger_eng.next_engine
self.engine.next_engine = oldnext
drop_engine_after(self.engine)
17 changes: 7 additions & 10 deletions projectq/meta/_loop.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@

from copy import deepcopy

from projectq.ops import Allocate, Deallocate

from projectq.cengines import BasicEngine
from projectq.ops import Allocate, Deallocate
from ._util import insert_engine, drop_engine_after


class QubitManagementError(Exception):
Expand Down Expand Up @@ -241,19 +241,16 @@ def __init__(self, engine, num):
if num < 0:
raise ValueError("Number of loop iterations must be >=0.")
self.num = num
self._loop_eng = None

def __enter__(self):
if self.num != 1:
loop_eng = LoopEngine(self.num)
loop_eng.main_engine = self.engine.main_engine
oldnext = self.engine.next_engine
self.engine.next_engine = loop_eng
loop_eng.next_engine = oldnext
self._loop_eng = loop_eng
self._loop_eng = LoopEngine(self.num)
insert_engine(self.engine, self._loop_eng)

def __exit__(self, type, value, traceback):
if self.num != 1:
# remove loop handler from engine list (i.e. skip it)
self._loop_eng.run()
oldnext = self._loop_eng.next_engine
self.engine.next_engine = oldnext
self._loop_eng = None
drop_engine_after(self.engine)
45 changes: 45 additions & 0 deletions projectq/meta/_util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


def insert_engine(prev_engine, engine_to_insert):
"""
Inserts an engine into the singly-linked list of engines.
It also sets the correct main_engine for engine_to_insert.
Args:
prev_engine (projectq.cengines.BasicEngine):
The engine just before the insertion point.
engine_to_insert (projectq.cengines.BasicEngine):
The engine to insert at the insertion point.
"""
engine_to_insert.main_engine = prev_engine.main_engine
engine_to_insert.next_engine = prev_engine.next_engine
prev_engine.next_engine = engine_to_insert


def drop_engine_after(prev_engine):
"""
Removes an engine from the singly-linked list of engines.
Args:
prev_engine (projectq.cengines.BasicEngine):
The engine just before the engine to drop.
Returns:
Engine: The dropped engine.
"""
dropped_engine = prev_engine.next_engine
prev_engine.next_engine = dropped_engine.next_engine
dropped_engine.next_engine = None
dropped_engine.main_engine = None
return dropped_engine
46 changes: 46 additions & 0 deletions projectq/meta/_util_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


from projectq import MainEngine
from projectq.cengines import DummyEngine
from projectq.meta import insert_engine, drop_engine_after


def test_insert_then_drop():
d1 = DummyEngine()
d2 = DummyEngine()
d3 = DummyEngine()
eng = MainEngine(backend=d3, engine_list=[d1])

assert d1.next_engine is d3
assert d2.next_engine is None
assert d3.next_engine is None
assert d1.main_engine is eng
assert d2.main_engine is None
assert d3.main_engine is eng

insert_engine(d1, d2)
assert d1.next_engine is d2
assert d2.next_engine is d3
assert d3.next_engine is None
assert d1.main_engine is eng
assert d2.main_engine is eng
assert d3.main_engine is eng

drop_engine_after(d1)
assert d1.next_engine is d3
assert d2.next_engine is None
assert d3.next_engine is None
assert d1.main_engine is eng
assert d2.main_engine is None
assert d3.main_engine is eng

0 comments on commit 22259a1

Please sign in to comment.