Skip to content

Commit

Permalink
Done with cengines.
Browse files Browse the repository at this point in the history
  • Loading branch information
thomashaener committed Mar 19, 2017
1 parent d2a41ab commit d265c65
Show file tree
Hide file tree
Showing 18 changed files with 283 additions and 222 deletions.
66 changes: 38 additions & 28 deletions projectq/cengines/_basics.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from projectq.ops import Command
import projectq.cengines


class LastEngineException(Exception):
"""
Exception thrown when the last engine tries to access the next one.
Expand All @@ -25,10 +26,11 @@ class LastEngineException(Exception):
engine, this behavior needs to be adapted (see BasicEngine.isAvailable).
"""
def __init__(self, engine):
Exception.__init__(self, "\nERROR: Sending to next engine failed. " +
engine.__class__.__name__ + " as last engine?" +
"\nIf this is legal, please override"
"'isAvailable' to adapt its behavior.")
Exception.__init__(self, ("\nERROR: Sending to next engine failed. "
"{} as last engine?\nIf this is legal, "
"please override 'isAvailable' to adapt its"
" behavior."
).format(engine.__class__.__name__))


class BasicEngine(object):
Expand Down Expand Up @@ -70,8 +72,8 @@ def is_available(self, cmd):
True if the command can be executed.
Raises:
LastEngineException: If is_last_engine is True but is_available is not
implemented.
LastEngineException: If is_last_engine is True but is_available
is not implemented.
"""
if not self.is_last_engine:
return self.next_engine.is_available(cmd)
Expand All @@ -80,8 +82,8 @@ def is_available(self, cmd):

def allocate_qubit(self, dirty=False):
"""
Return a new qubit as a list containing 1 qubit object (quantum register
of size 1).
Return a new qubit as a list containing 1 qubit object (quantum
register of size 1).
Allocates a new qubit by getting a (new) qubit id from the MainEngine,
creating the qubit object, and then sending an AllocateQubit command
Expand All @@ -90,15 +92,15 @@ def allocate_qubit(self, dirty=False):
must be returned to their initial states before they are deallocated /
freed.
All allocated qubits are added to the MainEngine's set of active qubits
as weak references. This allows proper clean-up at the end of the Python
program (using atexit), deallocating all qubits which are still alive.
Qubit ids of dirty qubits are registered in MainEngine's dirty_qubits
set.
All allocated qubits are added to the MainEngine's set of active
qubits as weak references. This allows proper clean-up at the end of
the Python program (using atexit), deallocating all qubits which are
still alive. Qubit ids of dirty qubits are registered in MainEngine's
dirty_qubits set.
Args:
dirty (bool): If True, indicates that the allocated qubit may be dirty
(i.e., in an arbitrary initial state).
dirty (bool): If True, indicates that the allocated qubit may be
dirty (i.e., in an arbitrary initial state).
Returns:
Qureg of length 1, where the first entry is the allocated qubit.
Expand All @@ -109,11 +111,13 @@ def allocate_qubit(self, dirty=False):
from projectq.meta import DirtyQubitTag
if self.is_meta_tag_supported(DirtyQubitTag):
oldnext = self.next_engine

def cmd_modifier(cmd):
assert(cmd.gate == Allocate)
cmd.tags += [DirtyQubitTag()]
return cmd
self.next_engine = projectq.cengines.CommandModifier(cmd_modifier)
self.next_engine = projectq.cengines.CommandModifier(
cmd_modifier)
self.next_engine.next_engine = oldnext
self.send([Command(self, Allocate, (qb,))])
self.next_engine = oldnext
Expand All @@ -127,8 +131,8 @@ def cmd_modifier(cmd):

def allocate_qureg(self, n):
"""
Allocate n qubits and return them as a quantum register, which is a list
of qubit objects.
Allocate n qubits and return them as a quantum register, which is a
list of qubit objects.
Args:
n (int): Number of qubits to allocate
Expand All @@ -139,9 +143,9 @@ def allocate_qureg(self, n):

def deallocate_qubit(self, qubit):
"""
Deallocate a qubit (and sends the deallocation command down the pipeline).
If the qubit was allocated as a dirty qubit, add DirtyQubitTag() to
Deallocate command.
Deallocate a qubit (and sends the deallocation command down the
pipeline). If the qubit was allocated as a dirty qubit, add
DirtyQubitTag() to Deallocate command.
Args:
qubit (BasicQubit): Qubit to deallocate.
Expand All @@ -152,11 +156,13 @@ def deallocate_qubit(self, qubit):
else:
from projectq.meta import DirtyQubitTag
oldnext = self.next_engine

def cmd_modifier(cmd):
assert(cmd.gate == Deallocate)
cmd.tags += [DirtyQubitTag()]
return cmd
self.next_engine = projectq.cengines.CommandModifier(cmd_modifier)
self.next_engine = projectq.cengines.CommandModifier(
cmd_modifier)
self.next_engine.next_engine = oldnext
self.send([Command(self, Deallocate, ([qubit],))])
self.next_engine = oldnext
Expand All @@ -166,12 +172,14 @@ def is_meta_tag_supported(self, meta_tag):
Check if there is a compiler engine handling the meta tag
Args:
engine: First engine to check (then iteratively calls getNextEngine)
engine: First engine to check (then iteratively calls
getNextEngine)
meta_tag: Meta tag class for which to check support
Returns:
supported (bool): True if one of the further compiler engines is a meta tag handler, i.e.,
engine.is_meta_tag_handler(meta_tag) returns True.
supported (bool): True if one of the further compiler engines is a
meta tag handler, i.e., engine.is_meta_tag_handler(meta_tag)
returns True.
"""
engine = self
try:
Expand All @@ -187,7 +195,9 @@ def is_meta_tag_supported(self, meta_tag):

# sends the commandList to the next engine
def send(self, command_list):
""" Forward the list of commands to the next engine in the pipeline. """
"""
Forward the list of commands to the next engine in the pipeline.
"""
self.next_engine.receive(command_list)


Expand All @@ -206,8 +216,8 @@ def __init__(self, engine, cmd_mod_fun=None):
Args:
engine (BasicEngine): Engine to forward all commands to.
cmd_mod_fun (function): Function which is called before sending a
command. Each command cmd is replaced by the command it returns when
getting called with cmd.
command. Each command cmd is replaced by the command it
returns when getting called with cmd.
"""
BasicEngine.__init__(self)
self.main_engine = engine.main_engine
Expand Down
22 changes: 13 additions & 9 deletions projectq/cengines/_basics_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@

def test_basic_engine_init():
eng = _basics.BasicEngine()
assert eng.main_engine == None
assert eng.next_engine == None
assert eng.is_last_engine == False
assert eng.main_engine is None
assert eng.next_engine is None
assert not eng.is_last_engine


def test_basic_engine_is_available():
Expand All @@ -61,26 +61,28 @@ def test_basic_engine_allocate_and_deallocate_qubit_and_qureg():
# custom receive function which checks that main_engine does not send
# any allocate or deallocate gates
cmd_sent_by_main_engine = []

def receive(self, cmd_list):
cmd_sent_by_main_engine.append(cmd_list)

eng.receive = types.MethodType(receive, eng)
# Create test engines:
saving_backend = DummyEngine(save_commands=True)
main_engine = MainEngine(backend=saving_backend,
engine_list=[eng, DummyEngine()])
engine_list=[eng, DummyEngine()])
# Allocate and deallocate qubits
qubit = eng.allocate_qubit()
# Try to allocate dirty qubit but it should give a non dirty qubit
not_dirty_qubit = eng.allocate_qubit(dirty=True)

# Allocate an actual dirty qubit
def allow_dirty_qubits(self, meta_tag):
if meta_tag == DirtyQubitTag:
return True
return False

saving_backend.is_meta_tag_handler = types.MethodType(allow_dirty_qubits,
saving_backend)
saving_backend)
dirty_qubit = eng.allocate_qubit(dirty=True)
qureg = eng.allocate_qureg(2)
# Test qubit allocation
Expand All @@ -107,7 +109,7 @@ def allow_dirty_qubits(self, meta_tag):
assert id(tmp_qubit.engine) == id(eng)
# Test uniqueness of ids
assert len(set([qubit[0].id, not_dirty_qubit[0].id, dirty_qubit[0].id,
qureg[0].id, qureg[1].id])) == 5
qureg[0].id, qureg[1].id])) == 5
# Test allocate gates were sent
assert len(cmd_sent_by_main_engine) == 0
assert len(saving_backend.received_commands) == 5
Expand All @@ -130,6 +132,7 @@ def allow_dirty_qubits(self, meta_tag):
def test_basic_engine_is_meta_tag_supported():
eng = _basics.BasicEngine()
# BasicEngine needs receive function to function so let's add it:

def receive(self, cmd_list):
self.send(cmd_list)

Expand All @@ -145,9 +148,9 @@ def allow_dirty_qubits(self, meta_tag):
return False

engine2.is_meta_tag_handler = types.MethodType(allow_dirty_qubits,
engine2)
engine2)
main_engine = MainEngine(backend=backend,
engine_list=[engine0, engine1, engine2])
engine_list=[engine0, engine1, engine2])
assert not main_engine.is_meta_tag_supported("NotSupported")
assert main_engine.is_meta_tag_supported(DirtyQubitTag)

Expand All @@ -156,7 +159,8 @@ def test_forwarder_engine():
backend = DummyEngine(save_commands=True)
engine0 = DummyEngine()
main_engine = MainEngine(backend=backend,
engine_list = [engine0])
engine_list=[engine0])

def cmd_mod_fun(cmd):
cmd.tags = "NewTag"
return cmd
Expand Down
8 changes: 4 additions & 4 deletions projectq/cengines/_cmdmodifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ def __init__(self, cmd_mod_fun):
Initialize the CommandModifier.
Args:
cmd_mod_fun (function): Function which, given a command cmd, returns
the command it should send instead.
cmd_mod_fun (function): Function which, given a command cmd,
returns the command it should send instead.
Example:
.. code-block:: python
Expand All @@ -44,8 +44,8 @@ def cmd_mod_fun(cmd):

def receive(self, command_list):
"""
Receive a list of commands from the previous engine, modify all commands,
and send them on to the next engine.
Receive a list of commands from the previous engine, modify all
commands, and send them on to the next engine.
Args:
command_list (list<Command>): List of commands to receive and then
Expand Down
4 changes: 2 additions & 2 deletions projectq/cengines/_cmdmodifier_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ def cmd_mod_fun(cmd):
return cmd

backend = DummyEngine(save_commands=True)
main_engine = MainEngine(backend=backend,
engine_list=[_cmdmodifier.CommandModifier(cmd_mod_fun)])
cmd_modifier = _cmdmodifier.CommandModifier(cmd_mod_fun)
main_engine = MainEngine(backend=backend, engine_list=[cmd_modifier])
qubit = main_engine.allocate_qubit()
H | qubit
# Test if H gate was sent through forwarder_eng and tag was added
Expand Down

0 comments on commit d265c65

Please sign in to comment.