Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
804 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
# Copyright 2018 ProjectQ-Framework (www.projectq.ch) | ||
# | ||
# 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. | ||
|
||
""" | ||
Defines a setup to compile to qubits placed in a linear chain or a circle. | ||
It provides the `engine_list` for the `MainEngine`. This engine list contains | ||
an AutoReplacer with most of the gate decompositions of ProjectQ, which are | ||
used to decompose a circuit into only two qubit gates and arbitrary single | ||
qubit gates. ProjectQ's LinearMapper is then used to introduce the necessary | ||
Swap operations to route interacting qubits next to each other. This setup | ||
allows to choose the final gate set (with some limitations). | ||
""" | ||
|
||
import inspect | ||
|
||
import projectq | ||
import projectq.libs.math | ||
import projectq.setups.decompositions | ||
from projectq.cengines import (AutoReplacer, DecompositionRuleSet, | ||
InstructionFilter, LinearMapper, LocalOptimizer, | ||
TagRemover) | ||
from projectq.ops import (BasicMathGate, ClassicalInstructionGate, CNOT, | ||
ControlledGate, get_inverse, QFT, Swap) | ||
|
||
|
||
def high_level_gates(eng, cmd): | ||
""" | ||
Remove any MathGates. | ||
""" | ||
g = cmd.gate | ||
if g == QFT or get_inverse(g) == QFT or g == Swap: | ||
return True | ||
elif isinstance(g, BasicMathGate): | ||
return False | ||
return True | ||
|
||
|
||
def one_and_two_qubit_gates(eng, cmd): | ||
all_qubits = [q for qr in cmd.all_qubits for q in qr] | ||
if isinstance(cmd.gate, ClassicalInstructionGate): | ||
# This is required to allow Measure, Allocate, Deallocate, Flush | ||
return True | ||
elif len(all_qubits) <= 2: | ||
return True | ||
else: | ||
return False | ||
|
||
|
||
def get_engine_list(num_qubits, cyclic=False, one_qubit_gates="any", | ||
two_qubit_gates=(CNOT, Swap)): | ||
""" | ||
Returns an engine list to compile to a linear chain of qubits. | ||
Note: | ||
If you choose a new gate set for which the compiler does not yet have | ||
standard rules, it raises an `NoGateDecompositionError` or a | ||
`RuntimeError: maximum recursion depth exceeded...`. Also note that | ||
even the gate sets which work might not yet be optimized. So make sure | ||
to double check and potentially extend the decomposition rules. | ||
This implemention currently requires that the one qubit gates must | ||
contain Rz and at least one of {Ry(best), Rx, H} and the two qubit gate | ||
must contain CNOT. | ||
Note: | ||
Classical instructions gates such as e.g. Flush and Measure are | ||
automatically allowed. | ||
Example: | ||
get_engine_list(num_qubits=10, cyclic=False, | ||
one_qubit_gates=(Rz, Ry, Rx, H), | ||
two_qubit_gates=(CNOT,)) | ||
Args: | ||
num_qubits(int): Number of qubits in the chain | ||
cyclic(bool): If a circle or not. Default is False | ||
one_qubit_gates: "any" allows any one qubit gate, otherwise provide | ||
a tuple of the allowed gates. If the gates are | ||
instances of a class (e.g. X), it allows all gates | ||
which are equal to it. If the gate is a class (Rz), it | ||
allows all instances of this class. Default is "any" | ||
two_qubit_gates: "any" allows any two qubit gate, otherwise provide | ||
a tuple of the allowed gates. If the gates are | ||
instances of a class (e.g. CNOT), it allows all gates | ||
which are equal to it. If the gate is a class, it | ||
allows all instances of this class. | ||
Default is (CNOT, Swap). | ||
Raises: | ||
TypeError: If input is for the gates is not "any" or a tuple. | ||
Returns: | ||
A list of suitable compiler engines. | ||
""" | ||
if two_qubit_gates != "any" and not isinstance(two_qubit_gates, tuple): | ||
raise TypeError("two_qubit_gates parameter must be 'any' or a tuple. " | ||
"When supplying only one gate, make sure to correctly " | ||
"create the tuple (don't miss the comma), " | ||
"e.g. two_qubit_gates=(CNOT,)") | ||
if one_qubit_gates != "any" and not isinstance(one_qubit_gates, tuple): | ||
raise TypeError("one_qubit_gates parameter must be 'any' or a tuple.") | ||
|
||
rule_set = DecompositionRuleSet(modules=[projectq.libs.math, | ||
projectq.setups.decompositions]) | ||
allowed_gate_classes = [] | ||
allowed_gate_instances = [] | ||
if one_qubit_gates != "any": | ||
for gate in one_qubit_gates: | ||
if inspect.isclass(gate): | ||
allowed_gate_classes.append(gate) | ||
else: | ||
allowed_gate_instances.append((gate, 0)) | ||
if two_qubit_gates != "any": | ||
for gate in two_qubit_gates: | ||
if inspect.isclass(gate): | ||
# Controlled gate classes don't yet exists and would require | ||
# separate treatment | ||
assert not isinstance(gate, ControlledGate) | ||
allowed_gate_classes.append(gate) | ||
else: | ||
if isinstance(gate, ControlledGate): | ||
allowed_gate_instances.append((gate._gate, gate._n)) | ||
else: | ||
allowed_gate_instances.append((gate, 0)) | ||
allowed_gate_classes = tuple(allowed_gate_classes) | ||
allowed_gate_instances = tuple(allowed_gate_instances) | ||
|
||
def low_level_gates(eng, cmd): | ||
all_qubits = [q for qr in cmd.all_qubits for q in qr] | ||
assert len(all_qubits) <= 2 | ||
if isinstance(cmd.gate, ClassicalInstructionGate): | ||
# This is required to allow Measure, Allocate, Deallocate, Flush | ||
return True | ||
elif one_qubit_gates == "any" and len(all_qubits) == 1: | ||
return True | ||
elif two_qubit_gates == "any" and len(all_qubits) == 2: | ||
return True | ||
elif isinstance(cmd.gate, allowed_gate_classes): | ||
return True | ||
elif (cmd.gate, len(cmd.control_qubits)) in allowed_gate_instances: | ||
return True | ||
else: | ||
return False | ||
|
||
return [AutoReplacer(rule_set), | ||
TagRemover(), | ||
InstructionFilter(high_level_gates), | ||
LocalOptimizer(5), | ||
AutoReplacer(rule_set), | ||
TagRemover(), | ||
InstructionFilter(one_and_two_qubit_gates), | ||
LocalOptimizer(5), | ||
LinearMapper(num_qubits=num_qubits, cyclic=cyclic), | ||
AutoReplacer(rule_set), | ||
TagRemover(), | ||
InstructionFilter(low_level_gates), | ||
LocalOptimizer(5), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
# Copyright 2018 ProjectQ-Framework (www.projectq.ch) | ||
# | ||
# 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. | ||
|
||
"""Tests for projectq.setups.linear.""" | ||
|
||
import pytest | ||
|
||
import projectq | ||
from projectq.cengines import DummyEngine, LinearMapper | ||
from projectq.libs.math import AddConstant | ||
from projectq.ops import BasicGate, CNOT, H, Measure, Rx, Rz, Swap, X | ||
|
||
import projectq.setups.linear as linear_setup | ||
|
||
|
||
def test_mapper_present_and_correct_params(): | ||
found = False | ||
mapper = None | ||
for engine in linear_setup.get_engine_list(num_qubits=10, cyclic=True): | ||
if isinstance(engine, LinearMapper): | ||
mapper = engine | ||
found = True | ||
assert found | ||
assert mapper.num_qubits == 10 | ||
assert mapper.cyclic | ||
|
||
|
||
def test_parameter_any(): | ||
engine_list = linear_setup.get_engine_list(num_qubits=10, cyclic=False, | ||
one_qubit_gates="any", | ||
two_qubit_gates="any") | ||
backend = DummyEngine(save_commands=True) | ||
eng = projectq.MainEngine(backend, engine_list) | ||
qubit1 = eng.allocate_qubit() | ||
qubit2 = eng.allocate_qubit() | ||
gate = BasicGate() | ||
gate | (qubit1, qubit2) | ||
gate | qubit1 | ||
eng.flush() | ||
print(len(backend.received_commands)) | ||
assert backend.received_commands[2].gate == gate | ||
assert backend.received_commands[3].gate == gate | ||
|
||
|
||
def test_restriction(): | ||
engine_list = linear_setup.get_engine_list(num_qubits=10, cyclic=False, | ||
one_qubit_gates=(Rz, H), | ||
two_qubit_gates=(CNOT, | ||
AddConstant)) | ||
backend = DummyEngine(save_commands=True) | ||
eng = projectq.MainEngine(backend, engine_list) | ||
qubit1 = eng.allocate_qubit() | ||
qubit2 = eng.allocate_qubit() | ||
qubit3 = eng.allocate_qubit() | ||
eng.flush() | ||
CNOT | (qubit1, qubit2) | ||
H | qubit1 | ||
Rz(0.2) | qubit1 | ||
Measure | qubit1 | ||
Swap | (qubit1, qubit2) | ||
Rx(0.1) | (qubit1) | ||
AddConstant(1) | qubit1 + qubit2 + qubit3 | ||
eng.flush() | ||
assert backend.received_commands[4].gate == X | ||
assert len(backend.received_commands[4].control_qubits) == 1 | ||
assert backend.received_commands[5].gate == H | ||
assert backend.received_commands[6].gate == Rz(0.2) | ||
assert backend.received_commands[7].gate == Measure | ||
for cmd in backend.received_commands[7:]: | ||
assert cmd.gate != Swap | ||
assert not isinstance(cmd.gate, Rx) | ||
assert not isinstance(cmd.gate, AddConstant) | ||
|
||
|
||
def test_wrong_init(): | ||
with pytest.raises(TypeError): | ||
engine_list = linear_setup.get_engine_list(num_qubits=10, cyclic=False, | ||
one_qubit_gates="any", | ||
two_qubit_gates=(CNOT)) | ||
with pytest.raises(TypeError): | ||
engine_list = linear_setup.get_engine_list(num_qubits=10, cyclic=False, | ||
one_qubit_gates="Any", | ||
two_qubit_gates=(CNOT,)) |
Oops, something went wrong.