-
Notifications
You must be signed in to change notification settings - Fork 270
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
1 parent
536e1fe
commit e98e980
Showing
13 changed files
with
739 additions
and
5 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
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,34 @@ | ||
revkit | ||
====== | ||
|
||
This library integrates `RevKit <https://msoeken.github.io/revkit.html>`_ into | ||
ProjectQ to allow some automatic synthesis routines for reversible logic. The | ||
library adds the following operations that can be used to construct quantum | ||
circuits: | ||
|
||
- :class:`~projectq.libs.revkit.ControlFunctionOracle`: Synthesizes a reversible circuit from Boolean control function | ||
- :class:`~projectq.libs.revkit.PermutationOracle`: Synthesizes a reversible circuit for a permutation | ||
- :class:`~projectq.libs.revkit.PhaseOracle`: Synthesizes phase circuit from an arbitrary Boolean function | ||
|
||
RevKit can be installed from PyPi with `pip install revkit`. | ||
|
||
.. note:: | ||
|
||
The RevKit Python module must be installed in order to use this ProjectQ library. | ||
|
||
There exist precompiled binaries in PyPi, as well as a source distribution. | ||
Note that a C++ compiler with C++17 support is required to build the RevKit | ||
python module from source. Examples for compatible compilers are Clang | ||
6.0, GCC 7.3, and GCC 8.1. | ||
|
||
The integration of RevKit into ProjectQ and other quantum programming languages is described in the paper | ||
|
||
* Mathias Soeken, Thomas Haener, and Martin Roetteler "Programming Quantum Computers Using Design Automation," in: Design Automation and Test in Europe (2018) [`arXiv:1803.01022 <https://arxiv.org/abs/1803.01022>`_] | ||
|
||
Module contents | ||
--------------- | ||
|
||
.. automodule:: projectq.libs.revkit | ||
:members: | ||
:special-members: __init__,__or__ | ||
:imported-members: |
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
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,25 @@ | ||
from projectq.cengines import MainEngine | ||
from projectq.ops import All, H, X, Measure | ||
from projectq.meta import Compute, Uncompute | ||
from projectq.libs.revkit import PhaseOracle | ||
|
||
# phase function | ||
def f(a, b, c, d): | ||
return (a and b) ^ (c and d) | ||
|
||
eng = MainEngine() | ||
x1, x2, x3, x4 = qubits = eng.allocate_qureg(4) | ||
|
||
with Compute(eng): | ||
All(H) | qubits | ||
X | x1 | ||
PhaseOracle(f) | qubits | ||
Uncompute(eng) | ||
|
||
PhaseOracle(f) | qubits | ||
All(H) | qubits | ||
All(Measure) | qubits | ||
|
||
eng.flush() | ||
|
||
print("Shift is {}".format(8 * int(x4) + 4 * int(x3) + 2 * int(x2) + int(x1))) |
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,39 @@ | ||
from projectq.cengines import MainEngine | ||
from projectq.ops import All, H, X, CNOT, Measure | ||
from projectq.meta import Compute, Uncompute, Dagger | ||
from projectq.libs.revkit import PhaseOracle, PermutationOracle | ||
|
||
import revkit | ||
|
||
# phase function | ||
def f(a, b, c, d, e, f): | ||
return (a and b) ^ (c and d) ^ (e and f) | ||
|
||
# permutation | ||
pi = [0, 2, 3, 5, 7, 1, 4, 6] | ||
|
||
eng = MainEngine() | ||
qubits = eng.allocate_qureg(6) | ||
x = qubits[::2] # qubits on odd lines | ||
y = qubits[1::2] # qubits on even lines | ||
|
||
# circuit | ||
with Compute(eng): | ||
All(H) | qubits | ||
All(X) | [x[0], x[1]] | ||
PermutationOracle(pi) | y | ||
PhaseOracle(f) | qubits | ||
Uncompute(eng) | ||
|
||
with Compute(eng): | ||
with Dagger(eng): | ||
PermutationOracle(pi, synth = revkit.dbs) | x | ||
PhaseOracle(f) | qubits | ||
Uncompute(eng) | ||
|
||
All(H) | qubits | ||
|
||
All(Measure) | qubits | ||
|
||
# measurement result | ||
print("Shift is {}".format(sum(int(q) << i for i, q in enumerate(qubits)))) |
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,17 @@ | ||
# Copyright 2017 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. | ||
|
||
from ._permutation import PermutationOracle | ||
from ._control_function import ControlFunctionOracle | ||
from ._phase import PhaseOracle |
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,121 @@ | ||
# Copyright 2017 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. | ||
|
||
from projectq.ops import BasicGate | ||
|
||
from ._utils import _exec | ||
|
||
|
||
class ControlFunctionOracle: | ||
""" | ||
Synthesizes a negation controlled by an arbitrary control function. | ||
This creates a circuit for a NOT gate which is controlled by an arbitrary | ||
Boolean control function. The control function is provided as integer | ||
representation of the function's truth table in binary notation. For | ||
example, for the majority-of-three function, which truth table 11101000, | ||
the value for function can be, e.g., ``0b11101000``, ``0xe8``, or ``232``. | ||
Example: | ||
This example creates a circuit that causes to invert qubit ``d``, | ||
the majority-of-three function evaluates to true for the control | ||
qubits ``a``, ``b``, and ``c``. | ||
.. code-block:: python | ||
ControlFunctionOracle(0x8e) | ([a, b, c], d) | ||
""" | ||
|
||
def __init__(self, function, **kwargs): | ||
""" | ||
Initializes a control function oracle. | ||
Args: | ||
function (int): Function truth table. | ||
Keyword Args: | ||
synth: A RevKit synthesis command which creates a reversible | ||
circuit based on a truth table and requires no additional | ||
ancillae (e.g., ``revkit.esopbs``). Can also be a nullary | ||
lambda that calls several RevKit commands. | ||
**Default:** ``revkit.esopbs`` | ||
""" | ||
if isinstance(function, int): | ||
self.function = function | ||
else: | ||
try: | ||
import dormouse | ||
self.function = dormouse.to_truth_table(function) | ||
except ImportError: # pragma: no cover | ||
raise RuntimeError( | ||
"The dormouse library needs to be installed in order to " | ||
"automatically compile Python code into functions. Try " | ||
"to install dormouse with 'pip install dormouse'." | ||
) | ||
self.kwargs = kwargs | ||
|
||
self._check_function() | ||
|
||
def __or__(self, qubits): | ||
""" | ||
Applies control function to qubits (and synthesizes circuit). | ||
Args: | ||
qubits (tuple<Qureg>): Qubits to which the control function is | ||
being applied. The first `n` qubits are for | ||
the controls, the last qubit is for the | ||
target qubit. | ||
""" | ||
try: | ||
import revkit | ||
except ImportError: # pragma: no cover | ||
raise RuntimeError( | ||
"The RevKit Python library needs to be installed and in the " | ||
"PYTHONPATH in order to call this function") | ||
|
||
# convert qubits to tuple | ||
qs = [] | ||
for item in BasicGate.make_tuple_of_qureg(qubits): | ||
qs += item if isinstance(item, list) else [item] | ||
|
||
# function truth table cannot be larger than number of control qubits | ||
# allow | ||
if 2**(2**(len(qs) - 1)) <= self.function: | ||
raise AttributeError( | ||
"Function truth table exceeds number of control qubits") | ||
|
||
# create truth table from function integer | ||
hex_length = max(2**(len(qs) - 1) // 4, 1) | ||
revkit.tt(table="{0:#0{1}x}".format(self.function, hex_length)) | ||
|
||
# create reversible circuit from truth table | ||
self.kwargs.get("synth", revkit.esopbs)() | ||
|
||
# check whether circuit has correct signature | ||
if revkit.ps(mct=True, silent=True)['qubits'] != len(qs): | ||
raise RuntimeError("Generated circuit lines does not match " | ||
"provided qubits") | ||
|
||
# convert reversible circuit to ProjectQ code and execute it | ||
_exec(revkit.write_projectq(log=True)["contents"], qs) | ||
|
||
def _check_function(self): | ||
""" | ||
Checks whether function is valid. | ||
""" | ||
# function must be positive. We check in __or__ whether function is | ||
# too large | ||
if self.function < 0: | ||
raise AttributeError("Function must be a postive integer") |
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,75 @@ | ||
# Copyright 2017 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 libs.revkit._control_function.""" | ||
|
||
import pytest | ||
|
||
from projectq.types import Qubit | ||
from projectq import MainEngine | ||
from projectq.cengines import DummyEngine | ||
|
||
from projectq.libs.revkit import ControlFunctionOracle | ||
|
||
|
||
# run this test only if RevKit Python module can be loaded | ||
revkit = pytest.importorskip('revkit') | ||
|
||
|
||
def test_control_function_majority(): | ||
saving_backend = DummyEngine(save_commands=True) | ||
main_engine = MainEngine(backend=saving_backend, | ||
engine_list=[DummyEngine()]) | ||
|
||
qubit0 = main_engine.allocate_qubit() | ||
qubit1 = main_engine.allocate_qubit() | ||
qubit2 = main_engine.allocate_qubit() | ||
qubit3 = main_engine.allocate_qubit() | ||
|
||
ControlFunctionOracle(0xe8) | (qubit0, qubit1, qubit2, qubit3) | ||
|
||
assert len(saving_backend.received_commands) == 7 | ||
|
||
def test_control_function_majority_from_python(): | ||
dormouse = pytest.importorskip('dormouse') | ||
|
||
def maj(a, b, c): | ||
return (a and b) or (a and c) or (b and c) # pragma: no cover | ||
|
||
saving_backend = DummyEngine(save_commands=True) | ||
main_engine = MainEngine(backend=saving_backend, | ||
engine_list=[DummyEngine()]) | ||
|
||
qubit0 = main_engine.allocate_qubit() | ||
qubit1 = main_engine.allocate_qubit() | ||
qubit2 = main_engine.allocate_qubit() | ||
qubit3 = main_engine.allocate_qubit() | ||
|
||
ControlFunctionOracle(maj) | (qubit0, qubit1, qubit2, qubit3) | ||
|
||
|
||
def test_control_function_invalid_function(): | ||
main_engine = MainEngine(backend=DummyEngine(), | ||
engine_list=[DummyEngine()]) | ||
|
||
qureg = main_engine.allocate_qureg(3) | ||
|
||
with pytest.raises(AttributeError): | ||
ControlFunctionOracle(-42) | qureg | ||
|
||
with pytest.raises(AttributeError): | ||
ControlFunctionOracle(0x8e) | qureg | ||
|
||
with pytest.raises(RuntimeError): | ||
ControlFunctionOracle(0x8, synth=revkit.esopps) | qureg |
Oops, something went wrong.