diff --git a/.travis.yml b/.travis.yml index 8f72f6799..9da1101b4 100755 --- a/.travis.yml +++ b/.travis.yml @@ -48,7 +48,7 @@ install: - pip$PY install -e . # command to run tests -script: export OMP_NUM_THREADS=1 && pytest projectq --cov projectq +script: export OMP_NUM_THREADS=1 && pytest -W error projectq --cov projectq after_success: - coveralls diff --git a/README.rst b/README.rst index a1e69f365..79cb9a4e5 100755 --- a/README.rst +++ b/README.rst @@ -11,6 +11,12 @@ ProjectQ - An open source software framework for quantum computing :target: http://projectq.readthedocs.io/en/latest/?badge=latest :alt: Documentation Status +.. image:: https://badge.fury.io/py/projectq.svg + :target: https://badge.fury.io/py/projectq + +.. image:: https://img.shields.io/badge/python-2.7%2C%203.3%2C%203.4%2C%203.5%2C%203.6-brightgreen.svg + + ProjectQ is an open source effort for quantum computing. It features a compilation framework capable of diff --git a/docs/README.rst b/docs/README.rst new file mode 100644 index 000000000..8edb90a73 --- /dev/null +++ b/docs/README.rst @@ -0,0 +1,27 @@ +Documentation +============= + +.. image:: https://readthedocs.org/projects/projectq/badge/?version=latest + :target: http://projectq.readthedocs.io/en/latest/?badge=latest + :alt: Documentation Status + + +Our detailed code documentation can be found online at `Read the Docs `__ and gets updated automatically. Besides the latest code documentation, there are also previous and offline versions available for download. + +Building the docs locally +------------------------- + +Before submitting new code, please make sure that the new or changed docstrings render nicely by building the docs manually. To this end, one has to install sphinx and the Read the Docs theme: + +.. code-block:: bash + + python -m pip install sphinx + python -m pip install sphinx_rtd_theme + +To build the documentation, navigate to this folder and execute: + +.. code-block:: bash + + make clean html + +Open _build/html/index.html to view the docs. diff --git a/docs/tutorials.rst b/docs/tutorials.rst index b9621a191..289af1aef 100755 --- a/docs/tutorials.rst +++ b/docs/tutorials.rst @@ -45,6 +45,9 @@ ProjectQ comes with a high-performance quantum simulator written in C++. Please .. note:: Please use pip version v6.1.0 or higher as this ensures that dependencies are installed in the `correct order `_. +.. note:: + ProjectQ should be installed on each computer individually as the C++ simulator compilation creates binaries which are optimized for the specific hardware on which it is being installed (potentially using our AVX version and `-march=native`). Therefore, sharing the same ProjectQ installation across different hardware can cause problems. + Detailed instructions and OS-specific hints ------------------------------------------- diff --git a/examples/README.rst b/examples/README.rst index 648107f34..d22e3c6ea 100644 --- a/examples/README.rst +++ b/examples/README.rst @@ -22,3 +22,5 @@ Examples and tutorials in this folder 2. Take a look at the *simulator_tutorial.ipynb* for a detailed introduction to most of the features of our high performance quantum simulator. 3. Running on the IBM QE chip is explained in more details in *ibm_entangle.ipynb*. + +4. A small tutorial on the compiler is available in *compiler_tutorial.ipynb* which explains how to compile to a specific gate set. diff --git a/examples/compiler_tutorial.ipynb b/examples/compiler_tutorial.ipynb new file mode 100644 index 000000000..06d1ce13f --- /dev/null +++ b/examples/compiler_tutorial.ipynb @@ -0,0 +1,323 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "collapsed": true, + "deletable": true, + "editable": true + }, + "source": [ + "# ProjectQ Compiler Tutorial\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "deletable": true, + "editable": true + }, + "source": [ + "The aim of this short tutorial is to give a first introduction to the ProjectQ compiler. In particular, we will show how to specify the gate set to which the compiler should translate a quantum program. A more extended tutorial will follow soon. Please check out our [ProjectQ paper](http://arxiv.org/abs/1612.08091) for an introduction to the basic concepts behind our compiler." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "deletable": true, + "editable": true + }, + "source": [ + "## The default compiler" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "deletable": true, + "editable": true + }, + "source": [ + "To compile a quantum program, we begin by creating a compiler called `MainEngine` and specify the backend for which the compiler should translate the program. For the purpose of this tutorial, we will use a `CommandPrinter` as a backend to display the compiled algorithm. It works the same for all other backends such as, e.g., the simulator or an interface to real hardware.\n", + "\n", + "Let's write a small program:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": false, + "deletable": true, + "editable": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Allocate | Qureg[1]\n", + "Allocate | Qureg[0]\n", + "Allocate | Qureg[2]\n", + "Allocate | Qureg[3]\n", + "Cexp(-0.1j * (0.5 X0 Y1 Z2)) | ( Qureg[0], Qureg[1-3] )\n", + "QFT | Qureg[1-3]\n", + "Rx(0.1) | Qureg[0]\n", + "CX | ( Qureg[0], Qureg[1] )\n", + "Measure | Qureg[1]\n", + "Measure | Qureg[2]\n", + "Measure | Qureg[3]\n", + "Measure | Qureg[0]\n", + "Deallocate | Qureg[0]\n", + "Deallocate | Qureg[3]\n", + "Deallocate | Qureg[2]\n", + "Deallocate | Qureg[1]\n" + ] + } + ], + "source": [ + "import projectq\n", + "from projectq.backends import CommandPrinter\n", + "from projectq.meta import Control\n", + "from projectq.ops import All, CNOT, Measure, QFT, QubitOperator, Rx, TimeEvolution, X\n", + "\n", + "# create the compiler and specify the backend:\n", + "eng = projectq.MainEngine(backend=CommandPrinter(accept_input=False))\n", + "\n", + "def my_quantum_program(eng):\n", + " qubit = eng.allocate_qubit()\n", + " qureg = eng.allocate_qureg(3)\n", + " with Control(eng, qubit):\n", + " hamiltonian = 0.5 * QubitOperator(\"X0 Y1 Z2\")\n", + " TimeEvolution(0.1, hamiltonian) | qureg\n", + " QFT | qureg\n", + " Rx(0.1) | qubit\n", + " CNOT | (qubit, qureg[0])\n", + " All(Measure) | qureg\n", + " Measure | qubit\n", + " eng.flush()\n", + "my_quantum_program(eng)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "deletable": true, + "editable": true + }, + "source": [ + "In the above example, the compiler did nothing because the default compiler (when `MainEngine` is called without a specific `engine_list` parameter) translates the individual gates to the gate set supported by the backend. In our case, the backend is a `CommandPrinter` which supports any type of gate.\n", + "\n", + "We can check what happens when the backend is a `Simulator` by inserting a `CommandPrinter` as a last compiler engine before the backend so that every command is printed before it gets sent to the Simulator: " + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "collapsed": false, + "deletable": true, + "editable": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Allocate | Qureg[1]\n", + "Allocate | Qureg[0]\n", + "Allocate | Qureg[2]\n", + "Allocate | Qureg[3]\n", + "Cexp(-0.1j * (0.5 X0 Y1 Z2)) | ( Qureg[0], Qureg[1-3] )\n", + "H | Qureg[3]\n", + "CR(1.5707963267948966) | ( Qureg[2], Qureg[3] )\n", + "CR(0.7853981633974483) | ( Qureg[1], Qureg[3] )\n", + "H | Qureg[2]\n", + "CR(1.5707963267948966) | ( Qureg[1], Qureg[2] )\n", + "H | Qureg[1]\n", + "Rx(0.1) | Qureg[0]\n", + "CX | ( Qureg[0], Qureg[1] )\n", + "Measure | Qureg[1]\n", + "Measure | Qureg[2]\n", + "Measure | Qureg[3]\n", + "Measure | Qureg[0]\n", + "Deallocate | Qureg[0]\n", + "Deallocate | Qureg[3]\n", + "Deallocate | Qureg[2]\n", + "Deallocate | Qureg[1]\n" + ] + } + ], + "source": [ + "from projectq.backends import Simulator\n", + "\n", + "# Use the default compiler engines with a CommandPrinter in the end:\n", + "engines2 = projectq.default_engines() + [CommandPrinter()]\n", + "\n", + "eng2 = projectq.MainEngine(backend=Simulator(), engine_list=engines2)\n", + "my_quantum_program(eng2)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "deletable": true, + "editable": true + }, + "source": [ + "As one can see, in this case the compiler had to do a little work because the Simulator does not support a QFT gate. Therefore, it automatically replaces the QFT gate by a sequence of lower-level gates." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "deletable": true, + "editable": true + }, + "source": [ + "## Specifying a particular gate set" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "deletable": true, + "editable": true + }, + "source": [ + "In this short example, we want to explore how to specify a particular gate set, which may be even more restrictive than what the backend naturally supports. This is useful, e.g., to obtain resource estimates for running a given program on actual quantum hardware which, in this example, can only perform CNOT and single qubit gates. All one has to do is insert an `InstructionFilter` into the `engine_list`:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "collapsed": false, + "deletable": true, + "editable": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Allocate | Qureg[3]\n", + "Allocate | Qureg[2]\n", + "Rx(1.5707963267948966) | Qureg[2]\n", + "Allocate | Qureg[1]\n", + "H | Qureg[1]\n", + "CX | ( Qureg[1], Qureg[2] )\n", + "CX | ( Qureg[2], Qureg[3] )\n", + "Rz(0.05) | Qureg[3]\n", + "Allocate | Qureg[0]\n", + "CX | ( Qureg[0], Qureg[3] )\n", + "Rz(12.516370614359172) | Qureg[3]\n", + "CX | ( Qureg[0], Qureg[3] )\n", + "CX | ( Qureg[2], Qureg[3] )\n", + "CX | ( Qureg[1], Qureg[2] )\n", + "H | Qureg[1]\n", + "R(0.39269908169872414) | Qureg[1]\n", + "H | Qureg[3]\n", + "Rz(0.7853981633974483) | Qureg[3]\n", + "Rx(10.995574287564276) | Qureg[2]\n", + "R(0.7853981633974483) | Qureg[2]\n", + "CX | ( Qureg[2], Qureg[3] )\n", + "Rz(11.780972450961723) | Qureg[3]\n", + "CX | ( Qureg[2], Qureg[3] )\n", + "Rz(0.39269908169872414) | Qureg[3]\n", + "CX | ( Qureg[1], Qureg[3] )\n", + "Rz(12.173671532660448) | Qureg[3]\n", + "CX | ( Qureg[1], Qureg[3] )\n", + "R(0.7853981633974483) | Qureg[1]\n", + "H | Qureg[2]\n", + "Rz(0.7853981633974483) | Qureg[2]\n", + "CX | ( Qureg[1], Qureg[2] )\n", + "Rz(11.780972450961723) | Qureg[2]\n", + "CX | ( Qureg[1], Qureg[2] )\n", + "H | Qureg[1]\n", + "Rx(0.1) | Qureg[0]\n", + "CX | ( Qureg[0], Qureg[1] )\n", + "Measure | Qureg[1]\n", + "Measure | Qureg[2]\n", + "Measure | Qureg[3]\n", + "Measure | Qureg[0]\n", + "Deallocate | Qureg[0]\n", + "Deallocate | Qureg[3]\n", + "Deallocate | Qureg[2]\n", + "Deallocate | Qureg[1]\n" + ] + } + ], + "source": [ + "from projectq.cengines import InstructionFilter\n", + "from projectq.ops import ClassicalInstructionGate\n", + "\n", + "# Write a function which, given a Command object, returns whether the command is supported:\n", + "def is_supported(eng, cmd):\n", + " if isinstance(cmd.gate, ClassicalInstructionGate):\n", + " # This is required to allow Measure, Allocate, Deallocate, Flush\n", + " return True\n", + " elif isinstance(cmd.gate, X.__class__) and len(cmd.control_qubits) == 1:\n", + " # Allows a CNOT gate which is an X gate with one control qubit\n", + " return True\n", + " elif (len(cmd.control_qubits) == 0 and \n", + " len(cmd.qubits) == 1 and\n", + " len(cmd.qubits[0]) == 1):\n", + " # Gate which has no control qubits, applied to 1 qureg consisting of 1 qubit\n", + " return True\n", + " else:\n", + " return False\n", + "\n", + "supported_gate_set_filter = InstructionFilter(is_supported)\n", + "\n", + "# Append the instruction filter to the list of compiler engines:\n", + "engines3 = projectq.default_engines() + [supported_gate_set_filter]\n", + "\n", + "eng3 = projectq.MainEngine(backend=CommandPrinter(accept_input=False), engine_list=engines3)\n", + "my_quantum_program(eng3)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "deletable": true, + "editable": true + }, + "source": [ + "As we can see, the compiler now needs to do a little more work. In some cases, the compiler does not know how to translate a command according to the specified gate set and it will throw a `NoGateDecompositionError`. This means one needs to implement a rule specifying how to decompose said command. See projectq/setups/decompositions for a few examples. This will be explained in a more extended tutorial." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true, + "deletable": true, + "editable": true + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.4.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/shor.py b/examples/shor.py index 0eebe9239..5e6b72f61 100755 --- a/examples/shor.py +++ b/examples/shor.py @@ -3,7 +3,11 @@ import math import random import sys -from fractions import Fraction, gcd +from fractions import Fraction +try: + from math import gcd +except ImportError: + from fractions import gcd from builtins import input diff --git a/projectq/__init__.py b/projectq/__init__.py index b404762c9..1707b15ad 100755 --- a/projectq/__init__.py +++ b/projectq/__init__.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/_version.py b/projectq/_version.py index e30f35a4a..a0c6d59e5 100755 --- a/projectq/_version.py +++ b/projectq/_version.py @@ -1,3 +1,5 @@ +# 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 @@ -11,4 +13,4 @@ # limitations under the License. """Define version number here and read it from setup.py automatically""" -__version__ = "0.3.3" +__version__ = "0.3.4" diff --git a/projectq/backends/__init__.py b/projectq/backends/__init__.py index be0501f21..6a3319779 100755 --- a/projectq/backends/__init__.py +++ b/projectq/backends/__init__.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/backends/_circuits/__init__.py b/projectq/backends/_circuits/__init__.py index 36cf265b5..1f22faec4 100755 --- a/projectq/backends/_circuits/__init__.py +++ b/projectq/backends/_circuits/__init__.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/backends/_circuits/_drawer.py b/projectq/backends/_circuits/_drawer.py index 5e75b2f8b..0f50610fa 100755 --- a/projectq/backends/_circuits/_drawer.py +++ b/projectq/backends/_circuits/_drawer.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/backends/_circuits/_drawer_test.py b/projectq/backends/_circuits/_drawer_test.py index 28f57bfb8..7df4bd0ee 100755 --- a/projectq/backends/_circuits/_drawer_test.py +++ b/projectq/backends/_circuits/_drawer_test.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/backends/_circuits/_to_latex.py b/projectq/backends/_circuits/_to_latex.py index 2a1f533bd..7d0f1a42f 100755 --- a/projectq/backends/_circuits/_to_latex.py +++ b/projectq/backends/_circuits/_to_latex.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/backends/_circuits/_to_latex_test.py b/projectq/backends/_circuits/_to_latex_test.py index 1c2288227..cafa93b19 100755 --- a/projectq/backends/_circuits/_to_latex_test.py +++ b/projectq/backends/_circuits/_to_latex_test.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/backends/_ibm/__init__.py b/projectq/backends/_ibm/__init__.py index 2a60a5487..289b40833 100755 --- a/projectq/backends/_ibm/__init__.py +++ b/projectq/backends/_ibm/__init__.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/backends/_ibm/_ibm.py b/projectq/backends/_ibm/_ibm.py index f4addc932..5f0b453e8 100755 --- a/projectq/backends/_ibm/_ibm.py +++ b/projectq/backends/_ibm/_ibm.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/backends/_ibm/_ibm_http_client.py b/projectq/backends/_ibm/_ibm_http_client.py index c85bbd468..8070970bc 100755 --- a/projectq/backends/_ibm/_ibm_http_client.py +++ b/projectq/backends/_ibm/_ibm_http_client.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/backends/_ibm/_ibm_http_client_test.py b/projectq/backends/_ibm/_ibm_http_client_test.py index 16859c770..34d05c4d1 100755 --- a/projectq/backends/_ibm/_ibm_http_client_test.py +++ b/projectq/backends/_ibm/_ibm_http_client_test.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/backends/_ibm/_ibm_test.py b/projectq/backends/_ibm/_ibm_test.py index 2352aac65..fbef4fa83 100755 --- a/projectq/backends/_ibm/_ibm_test.py +++ b/projectq/backends/_ibm/_ibm_test.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/backends/_printer.py b/projectq/backends/_printer.py index 3560f4e8a..b11d8057c 100755 --- a/projectq/backends/_printer.py +++ b/projectq/backends/_printer.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/backends/_printer_test.py b/projectq/backends/_printer_test.py index e8dfbed44..cadb0f28d 100755 --- a/projectq/backends/_printer_test.py +++ b/projectq/backends/_printer_test.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/backends/_resource.py b/projectq/backends/_resource.py index 6105355c2..cc5b3ecae 100755 --- a/projectq/backends/_resource.py +++ b/projectq/backends/_resource.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/backends/_resource_test.py b/projectq/backends/_resource_test.py index d2760aab4..9f9301201 100755 --- a/projectq/backends/_resource_test.py +++ b/projectq/backends/_resource_test.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/backends/_sim/__init__.py b/projectq/backends/_sim/__init__.py index 76f2443df..d225b59e0 100755 --- a/projectq/backends/_sim/__init__.py +++ b/projectq/backends/_sim/__init__.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/backends/_sim/_classical_simulator.py b/projectq/backends/_sim/_classical_simulator.py index 9f1f265bf..fb075b267 100755 --- a/projectq/backends/_sim/_classical_simulator.py +++ b/projectq/backends/_sim/_classical_simulator.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/backends/_sim/_classical_simulator_test.py b/projectq/backends/_sim/_classical_simulator_test.py index 9878c4f95..7f7b8db51 100755 --- a/projectq/backends/_sim/_classical_simulator_test.py +++ b/projectq/backends/_sim/_classical_simulator_test.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/backends/_sim/_cppkernels/fusion.hpp b/projectq/backends/_sim/_cppkernels/fusion.hpp index 326d11019..6539ea91d 100755 --- a/projectq/backends/_sim/_cppkernels/fusion.hpp +++ b/projectq/backends/_sim/_cppkernels/fusion.hpp @@ -1,3 +1,5 @@ +// 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 diff --git a/projectq/backends/_sim/_cppkernels/intrin/cintrin.hpp b/projectq/backends/_sim/_cppkernels/intrin/cintrin.hpp index 3e4229a30..4319ada2f 100755 --- a/projectq/backends/_sim/_cppkernels/intrin/cintrin.hpp +++ b/projectq/backends/_sim/_cppkernels/intrin/cintrin.hpp @@ -1,3 +1,5 @@ +// 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 diff --git a/projectq/backends/_sim/_cppkernels/intrin/kernel1.hpp b/projectq/backends/_sim/_cppkernels/intrin/kernel1.hpp index e5723b711..3ca031ab2 100755 --- a/projectq/backends/_sim/_cppkernels/intrin/kernel1.hpp +++ b/projectq/backends/_sim/_cppkernels/intrin/kernel1.hpp @@ -1,3 +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. + template inline void kernel_core(V &psi, std::size_t I, std::size_t d0, M const& m, M const& mt) { diff --git a/projectq/backends/_sim/_cppkernels/intrin/kernel2.hpp b/projectq/backends/_sim/_cppkernels/intrin/kernel2.hpp index b0d5c7e83..b355acd32 100755 --- a/projectq/backends/_sim/_cppkernels/intrin/kernel2.hpp +++ b/projectq/backends/_sim/_cppkernels/intrin/kernel2.hpp @@ -1,3 +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. + template inline void kernel_core(V &psi, std::size_t I, std::size_t d0, std::size_t d1, M const& m, M const& mt) { diff --git a/projectq/backends/_sim/_cppkernels/intrin/kernel3.hpp b/projectq/backends/_sim/_cppkernels/intrin/kernel3.hpp index 8d9d8369b..7f20db0d4 100755 --- a/projectq/backends/_sim/_cppkernels/intrin/kernel3.hpp +++ b/projectq/backends/_sim/_cppkernels/intrin/kernel3.hpp @@ -1,3 +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. + template inline void kernel_core(V &psi, std::size_t I, std::size_t d0, std::size_t d1, std::size_t d2, M const& m, M const& mt) { diff --git a/projectq/backends/_sim/_cppkernels/intrin/kernel4.hpp b/projectq/backends/_sim/_cppkernels/intrin/kernel4.hpp index f72c69c1c..9ff66eca3 100755 --- a/projectq/backends/_sim/_cppkernels/intrin/kernel4.hpp +++ b/projectq/backends/_sim/_cppkernels/intrin/kernel4.hpp @@ -1,3 +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. + template inline void kernel_core(V &psi, std::size_t I, std::size_t d0, std::size_t d1, std::size_t d2, std::size_t d3, M const& m, M const& mt) { diff --git a/projectq/backends/_sim/_cppkernels/intrin/kernel5.hpp b/projectq/backends/_sim/_cppkernels/intrin/kernel5.hpp index 28a05da17..6fc6cf751 100755 --- a/projectq/backends/_sim/_cppkernels/intrin/kernel5.hpp +++ b/projectq/backends/_sim/_cppkernels/intrin/kernel5.hpp @@ -1,3 +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. + template inline void kernel_core(V &psi, std::size_t I, std::size_t d0, std::size_t d1, std::size_t d2, std::size_t d3, std::size_t d4, M const& m, M const& mt) { diff --git a/projectq/backends/_sim/_cppkernels/intrin/kernels.hpp b/projectq/backends/_sim/_cppkernels/intrin/kernels.hpp index 9b11d46d2..e59c94168 100755 --- a/projectq/backends/_sim/_cppkernels/intrin/kernels.hpp +++ b/projectq/backends/_sim/_cppkernels/intrin/kernels.hpp @@ -1,3 +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. + #include #include #include diff --git a/projectq/backends/_sim/_cppkernels/nointrin/kernel1.hpp b/projectq/backends/_sim/_cppkernels/nointrin/kernel1.hpp index dbc500b34..bf3bf5a40 100755 --- a/projectq/backends/_sim/_cppkernels/nointrin/kernel1.hpp +++ b/projectq/backends/_sim/_cppkernels/nointrin/kernel1.hpp @@ -1,3 +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. + template inline void kernel_core(V &psi, std::size_t I, std::size_t d0, M const& m) { diff --git a/projectq/backends/_sim/_cppkernels/nointrin/kernel2.hpp b/projectq/backends/_sim/_cppkernels/nointrin/kernel2.hpp index 0d17c8380..98809d97c 100755 --- a/projectq/backends/_sim/_cppkernels/nointrin/kernel2.hpp +++ b/projectq/backends/_sim/_cppkernels/nointrin/kernel2.hpp @@ -1,3 +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. + template inline void kernel_core(V &psi, std::size_t I, std::size_t d0, std::size_t d1, M const& m) { diff --git a/projectq/backends/_sim/_cppkernels/nointrin/kernel3.hpp b/projectq/backends/_sim/_cppkernels/nointrin/kernel3.hpp index c7c69fb56..8d79f55fc 100755 --- a/projectq/backends/_sim/_cppkernels/nointrin/kernel3.hpp +++ b/projectq/backends/_sim/_cppkernels/nointrin/kernel3.hpp @@ -1,3 +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. + template inline void kernel_core(V &psi, std::size_t I, std::size_t d0, std::size_t d1, std::size_t d2, M const& m) { diff --git a/projectq/backends/_sim/_cppkernels/nointrin/kernel4.hpp b/projectq/backends/_sim/_cppkernels/nointrin/kernel4.hpp index b416b48bf..9e0e9ee51 100755 --- a/projectq/backends/_sim/_cppkernels/nointrin/kernel4.hpp +++ b/projectq/backends/_sim/_cppkernels/nointrin/kernel4.hpp @@ -1,3 +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. + template inline void kernel_core(V &psi, std::size_t I, std::size_t d0, std::size_t d1, std::size_t d2, std::size_t d3, M const& m) { diff --git a/projectq/backends/_sim/_cppkernels/nointrin/kernel5.hpp b/projectq/backends/_sim/_cppkernels/nointrin/kernel5.hpp index 43e62e185..9480eaa65 100755 --- a/projectq/backends/_sim/_cppkernels/nointrin/kernel5.hpp +++ b/projectq/backends/_sim/_cppkernels/nointrin/kernel5.hpp @@ -1,3 +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. + template inline void kernel_core(V &psi, std::size_t I, std::size_t d0, std::size_t d1, std::size_t d2, std::size_t d3, std::size_t d4, M const& m) { diff --git a/projectq/backends/_sim/_cppkernels/nointrin/kernels.hpp b/projectq/backends/_sim/_cppkernels/nointrin/kernels.hpp index 536eaf9d6..51026b812 100755 --- a/projectq/backends/_sim/_cppkernels/nointrin/kernels.hpp +++ b/projectq/backends/_sim/_cppkernels/nointrin/kernels.hpp @@ -1,3 +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. + #include #include #include diff --git a/projectq/backends/_sim/_cppkernels/simulator.hpp b/projectq/backends/_sim/_cppkernels/simulator.hpp index 54edf4406..ff58a38d0 100755 --- a/projectq/backends/_sim/_cppkernels/simulator.hpp +++ b/projectq/backends/_sim/_cppkernels/simulator.hpp @@ -1,3 +1,5 @@ +// 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 @@ -41,6 +43,7 @@ class Simulator{ using RndEngine = std::mt19937; using Term = std::vector>; using TermsDict = std::vector>; + using ComplexTermsDict = std::vector>; Simulator(unsigned seed = 1) : N_(0), vec_(1,0.), fusion_qubits_min_(4), fusion_qubits_max_(5), rnd_eng_(seed) { @@ -203,7 +206,6 @@ class Simulator{ } else fused_gates_ = fused_gates; - //run(); } template @@ -265,6 +267,22 @@ class Simulator{ return expectation; } + void apply_qubit_operator(ComplexTermsDict const& td, std::vector const& ids){ + run(); + auto new_state = StateVector(vec_.size(), 0.); + auto current_state = vec_; + for (auto const& term : td){ + auto const& coefficient = term.second; + apply_term(term.first, ids, {}); + #pragma omp parallel for schedule(static) + for (std::size_t i = 0; i < vec_.size(); ++i){ + new_state[i] += coefficient * vec_[i]; + vec_[i] = current_state[i]; + } + } + vec_ = std::move(new_state); + } + calc_type get_probability(std::vector const& bit_string, std::vector const& ids){ run(); diff --git a/projectq/backends/_sim/_cppsim.cpp b/projectq/backends/_sim/_cppsim.cpp index 46c7e0a53..74498d4e2 100755 --- a/projectq/backends/_sim/_cppsim.cpp +++ b/projectq/backends/_sim/_cppsim.cpp @@ -51,6 +51,7 @@ PYBIND11_PLUGIN(_cppsim) { .def("apply_controlled_gate", &Simulator::apply_controlled_gate) .def("emulate_math", &emulate_math_wrapper) .def("get_expectation_value", &Simulator::get_expectation_value) + .def("apply_qubit_operator", &Simulator::apply_qubit_operator) .def("emulate_time_evolution", &Simulator::emulate_time_evolution) .def("get_probability", &Simulator::get_probability) .def("get_amplitude", &Simulator::get_amplitude) diff --git a/projectq/backends/_sim/_pysim.py b/projectq/backends/_sim/_pysim.py index a9d8ad596..ac1c855cb 100755 --- a/projectq/backends/_sim/_pysim.py +++ b/projectq/backends/_sim/_pysim.py @@ -1,3 +1,5 @@ +# 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 @@ -248,6 +250,23 @@ def get_expectation_value(self, terms_dict, ids): self._state = _np.copy(current_state) return expectation + def apply_qubit_operator(self, terms_dict, ids): + """ + Apply a (possibly non-unitary) qubit operator to qubits. + + Args: + terms_dict (dict): Operator dictionary (see QubitOperator.terms) + ids (list[int]): List of qubit ids upon which the operator acts. + """ + new_state = _np.zeros_like(self._state) + current_state = _np.copy(self._state) + for (term, coefficient) in terms_dict: + self._apply_term(term, ids) + self._state *= coefficient + new_state += self._state + self._state = _np.copy(current_state) + self._state = new_state + def get_probability(self, bit_string, ids): """ Return the probability of the outcome `bit_string` when measuring @@ -355,27 +374,41 @@ def emulate_time_evolution(self, terms_dict, time, ids, ctrlids): def apply_controlled_gate(self, m, ids, ctrlids): """ - Applies the single qubit gate matrix m to the qubit with index ids[0], + Applies the k-qubit gate matrix m to the qubits with indices ids, using ctrlids as control qubits. Args: - m (list): 2x2 complex matrix describing the single-qubit + m (list[list]): 2^k x 2^k complex matrix describing the k-qubit gate. - ids (list): A list containing the qubit ID to which to apply the + ids (list): A list containing the qubit IDs to which to apply the gate. ctrlids (list): A list of control qubit IDs (i.e., the gate is only applied where these qubits are 1). """ - ID = ids[0] - pos = self._map[ID] - mask = self._get_control_mask(ctrlids) + if len(m) == 2: + pos = self._map[ids[0]] + self._single_qubit_gate(m, pos, mask) + else: + pos = [self._map[ID] for ID in ids] + self._multi_qubit_gate(m, pos, mask) + + def _single_qubit_gate(self, m, pos, mask): + """ + Applies the single qubit gate matrix m to the qubit at position `pos` + using `mask` to identify control qubits. + Args: + m (list[list]): 2x2 complex matrix describing the single-qubit + gate. + pos (int): Bit-position of the qubit. + mask (int): Bit-mask where set bits indicate control qubits. + """ def kernel(u, d, m): return u * m[0][0] + d * m[0][1], u * m[1][0] + d * m[1][1] for i in range(0, len(self._state), (1 << (pos + 1))): - for j in range(0, 1 << pos): + for j in range(1 << pos): if ((i + j) & mask) == mask: id1 = i + j id2 = id1 + (1 << pos) @@ -384,6 +417,41 @@ def kernel(u, d, m): self._state[id2], m) + def _multi_qubit_gate(self, m, pos, mask): + """ + Applies the k-qubit gate matrix m to the qubits at `pos` + using `mask` to identify control qubits. + + Args: + m (list[list]): 2^k x 2^k complex matrix describing the k-qubit + gate. + pos (list[int]): List of bit-positions of the qubits. + mask (int): Bit-mask where set bits indicate control qubits. + """ + # follows the description in https://arxiv.org/abs/1704.01127 + inactive = [p for p in range(len(self._map)) if p not in pos] + + matrix = _np.matrix(m) + subvec = _np.zeros(1 << len(pos), dtype=complex) + subvec_idx = [0] * len(subvec) + for c in range(1 << len(inactive)): + # determine base index (state of inactive qubits) + base = 0 + for i in range(len(inactive)): + base |= ((c >> i) & 1) << inactive[i] + # check the control mask + if mask != (base & mask): + continue + # now gather all elements involved in mat-vec mul + for x in range(len(subvec_idx)): + offset = 0 + for i in range(len(pos)): + offset |= ((x >> i) & 1) << pos[i] + subvec_idx[x] = base | offset + subvec[x] = self._state[subvec_idx[x]] + # perform mat-vec mul + self._state[subvec_idx] = matrix.dot(subvec) + def set_wavefunction(self, wavefunction, ordering): """ Set wavefunction and qubit ordering. diff --git a/projectq/backends/_sim/_simulator.py b/projectq/backends/_sim/_simulator.py index 532d48d32..c02b72920 100755 --- a/projectq/backends/_sim/_simulator.py +++ b/projectq/backends/_sim/_simulator.py @@ -1,3 +1,5 @@ +# 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 @@ -87,7 +89,7 @@ def is_available(self, cmd): """ Specialized implementation of is_available: The simulator can deal with all arbitrarily-controlled single-qubit gates which provide a - gate-matrix (via gate.get_matrix()). + gate-matrix (via gate.matrix). Args: cmd (Command): Command for which to check availability (single- @@ -103,7 +105,8 @@ def is_available(self, cmd): return True try: m = cmd.gate.matrix - if len(m) > 2: + # Allow up to 5-qubit gates + if len(m) > 2 ** 5: return False return True except: @@ -140,6 +143,40 @@ def get_expectation_value(self, qubit_operator, qureg): return self._simulator.get_expectation_value(operator, [qb.id for qb in qureg]) + def apply_qubit_operator(self, qubit_operator, qureg): + """ + Apply a (possibly non-unitary) qubit_operator to the current wave + function represented by the supplied quantum register. + + Args: + qubit_operator (projectq.ops.QubitOperator): Operator to apply. + qureg (list[Qubit],Qureg): Quantum bits to which to apply the + operator. + + Warning: + This function allows applying non-unitary gates and it will not + re-normalize the wave function! It is for numerical experiments + only and should not be used for other purposes. + + Note: + Make sure all previous commands (especially allocations) have + passed through the compilation chain (call main_engine.flush() to + make sure). + + Raises: + Exception: If `qubit_operator` acts on more qubits than present in + the `qureg` argument. + """ + num_qubits = len(qureg) + for term, _ in qubit_operator.terms.items(): + if not term == () and term[-1][0] >= num_qubits: + raise Exception("qubit_operator acts on more qubits than " + "contained in the qureg.") + operator = [(list(term), coeff) for (term, coeff) + in qubit_operator.terms.items()] + return self._simulator.apply_qubit_operator(operator, + [qb.id for qb in qureg]) + def get_probability(self, bit_string, qureg): """ Return the probability of the outcome `bit_string` when measuring @@ -288,18 +325,19 @@ def _handle(self, cmd): qubitids = [qb.id for qb in cmd.qubits[0]] ctrlids = [qb.id for qb in cmd.control_qubits] self._simulator.emulate_time_evolution(op, t, qubitids, ctrlids) - elif len(cmd.gate.matrix) == 2: + elif len(cmd.gate.matrix) <= 2 ** 5: matrix = cmd.gate.matrix + ids = [qb.id for qr in cmd.qubits for qb in qr] self._simulator.apply_controlled_gate(matrix.tolist(), - [cmd.qubits[0][0].id], + ids, [qb.id for qb in cmd.control_qubits]) if not self._gate_fusion: self._simulator.run() else: - raise Exception("This simulator only supports controlled single-" - "qubit gates!\nPlease add an auto-replacer engine" - " to your list of compiler engines.") + raise Exception("This simulator only supports controlled k-qubit" + " gates with k < 6!\nPlease add an auto-replacer" + " engine to your list of compiler engines.") def receive(self, command_list): """ diff --git a/projectq/backends/_sim/_simulator_test.py b/projectq/backends/_sim/_simulator_test.py index d1aa2ef31..60f4d8036 100755 --- a/projectq/backends/_sim/_simulator_test.py +++ b/projectq/backends/_sim/_simulator_test.py @@ -1,3 +1,5 @@ +# 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 @@ -33,6 +35,7 @@ S, Rx, Ry, + Rz, CNOT, Toffoli, Measure, @@ -41,7 +44,7 @@ QubitOperator, TimeEvolution, All) -from projectq.meta import Control +from projectq.meta import Control, Dagger from projectq.backends import Simulator @@ -87,7 +90,7 @@ def matrix(self): return [[0, 1], [1, 0]] -class Mock2QubitGate(BasicGate): +class Mock6QubitGate(BasicGate): def __init__(self): BasicGate.__init__(self) self.cnt = 0 @@ -95,7 +98,7 @@ def __init__(self): @property def matrix(self): self.cnt += 1 - return [[0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]] + return numpy.eye(2 ** 6) class MockNoMatrixGate(BasicGate): @@ -128,7 +131,7 @@ def test_simulator_is_available(sim): assert sim.is_available(new_cmd) assert new_cmd.gate.cnt == 1 - new_cmd.gate = Mock2QubitGate() + new_cmd.gate = Mock6QubitGate() assert not sim.is_available(new_cmd) assert new_cmd.gate.cnt == 1 @@ -206,6 +209,44 @@ def test_simulator_emulation(sim): Measure | (qubit1 + qubit2 + qubit3) +def test_simulator_kqubit_gate(sim): + m1 = Rx(0.3).matrix + m2 = Rx(0.8).matrix + m3 = Ry(0.1).matrix + m4 = Rz(0.9).matrix.dot(Ry(-0.1).matrix) + m = numpy.kron(m4, numpy.kron(m3, numpy.kron(m2, m1))) + + class KQubitGate(BasicGate): + @property + def matrix(self): + return m + + eng = MainEngine(sim, []) + qureg = eng.allocate_qureg(4) + qubit = eng.allocate_qubit() + Rx(-0.3) | qureg[0] + Rx(-0.8) | qureg[1] + Ry(-0.1) | qureg[2] + Rz(-0.9) | qureg[3] + Ry(0.1) | qureg[3] + X | qubit + with Control(eng, qubit): + KQubitGate() | qureg + X | qubit + with Control(eng, qubit): + with Dagger(eng): + KQubitGate() | qureg + assert sim.get_amplitude('0' * 5, qubit + qureg) == pytest.approx(1.) + + class LargerGate(BasicGate): + @property + def matrix(self): + return numpy.eye(2 ** 6) + + with pytest.raises(Exception): + LargerGate() | (qureg + qubit) + + def test_simulator_probability(sim): eng = MainEngine(sim) qubits = eng.allocate_qureg(6) @@ -326,6 +367,43 @@ def test_simulator_expectation_exception(sim): sim.get_expectation_value(op3, qureg) +def test_simulator_applyqubitoperator_exception(sim): + eng = MainEngine(sim, []) + qureg = eng.allocate_qureg(3) + op = QubitOperator('Z2') + sim.apply_qubit_operator(op, qureg) + op2 = QubitOperator('Z3') + with pytest.raises(Exception): + sim.apply_qubit_operator(op2, qureg) + op3 = QubitOperator('Z1') + QubitOperator('X1 Y3') + with pytest.raises(Exception): + sim.apply_qubit_operator(op3, qureg) + + +def test_simulator_applyqubitoperator(sim): + eng = MainEngine(sim, []) + qureg = eng.allocate_qureg(3) + op = QubitOperator('X0 Y1 Z2') + sim.apply_qubit_operator(op, qureg) + X | qureg[0] + Y | qureg[1] + Z | qureg[2] + assert sim.get_amplitude('000', qureg) == pytest.approx(1.) + + H | qureg[0] + op_H = 1. / math.sqrt(2.) * (QubitOperator('X0') + QubitOperator('Z0')) + sim.apply_qubit_operator(op_H, [qureg[0]]) + assert sim.get_amplitude('000', qureg) == pytest.approx(1.) + + op_Proj0 = 0.5 * (QubitOperator('') + QubitOperator('Z0')) + op_Proj1 = 0.5 * (QubitOperator('') - QubitOperator('Z0')) + H | qureg[0] + sim.apply_qubit_operator(op_Proj0, [qureg[0]]) + assert sim.get_amplitude('000', qureg) == pytest.approx(1. / math.sqrt(2.)) + sim.apply_qubit_operator(op_Proj1, [qureg[0]]) + assert sim.get_amplitude('000', qureg) == pytest.approx(0.) + + def test_simulator_time_evolution(sim): N = 9 # number of qubits time_to_evolve = 1.1 # time to evolve for diff --git a/projectq/cengines/__init__.py b/projectq/cengines/__init__.py index fbaf4742b..06d54907a 100755 --- a/projectq/cengines/__init__.py +++ b/projectq/cengines/__init__.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/cengines/_basics.py b/projectq/cengines/_basics.py index c5ce41143..b9e95d026 100755 --- a/projectq/cengines/_basics.py +++ b/projectq/cengines/_basics.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/cengines/_basics_test.py b/projectq/cengines/_basics_test.py index dfd64395a..768b42ef8 100755 --- a/projectq/cengines/_basics_test.py +++ b/projectq/cengines/_basics_test.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/cengines/_cmdmodifier.py b/projectq/cengines/_cmdmodifier.py index 5012f0158..0a1df34c6 100755 --- a/projectq/cengines/_cmdmodifier.py +++ b/projectq/cengines/_cmdmodifier.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/cengines/_cmdmodifier_test.py b/projectq/cengines/_cmdmodifier_test.py index c192801c5..afc7e16a2 100755 --- a/projectq/cengines/_cmdmodifier_test.py +++ b/projectq/cengines/_cmdmodifier_test.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/cengines/_ibmcnotmapper.py b/projectq/cengines/_ibmcnotmapper.py index f1cfefd70..ebfc9b20a 100755 --- a/projectq/cengines/_ibmcnotmapper.py +++ b/projectq/cengines/_ibmcnotmapper.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/cengines/_ibmcnotmapper_test.py b/projectq/cengines/_ibmcnotmapper_test.py index f0da809d3..09112f6d2 100755 --- a/projectq/cengines/_ibmcnotmapper_test.py +++ b/projectq/cengines/_ibmcnotmapper_test.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/cengines/_main.py b/projectq/cengines/_main.py index 903f43758..8b6df2fa5 100755 --- a/projectq/cengines/_main.py +++ b/projectq/cengines/_main.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/cengines/_main_test.py b/projectq/cengines/_main_test.py index 778fd01a4..55c924e2f 100755 --- a/projectq/cengines/_main_test.py +++ b/projectq/cengines/_main_test.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/cengines/_optimize.py b/projectq/cengines/_optimize.py index 266b5dbe7..0495c78d7 100755 --- a/projectq/cengines/_optimize.py +++ b/projectq/cengines/_optimize.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/cengines/_optimize_test.py b/projectq/cengines/_optimize_test.py index e03216129..e0196f83b 100755 --- a/projectq/cengines/_optimize_test.py +++ b/projectq/cengines/_optimize_test.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/cengines/_replacer/__init__.py b/projectq/cengines/_replacer/__init__.py index b35d31e12..d1d7ba9a8 100755 --- a/projectq/cengines/_replacer/__init__.py +++ b/projectq/cengines/_replacer/__init__.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/cengines/_replacer/_decomposition_rule.py b/projectq/cengines/_replacer/_decomposition_rule.py index f5043f500..d742f24c5 100755 --- a/projectq/cengines/_replacer/_decomposition_rule.py +++ b/projectq/cengines/_replacer/_decomposition_rule.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/cengines/_replacer/_decomposition_rule_set.py b/projectq/cengines/_replacer/_decomposition_rule_set.py index bbcabaab4..d7d1a38cb 100755 --- a/projectq/cengines/_replacer/_decomposition_rule_set.py +++ b/projectq/cengines/_replacer/_decomposition_rule_set.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/cengines/_replacer/_decomposition_rule_test.py b/projectq/cengines/_replacer/_decomposition_rule_test.py index 7fffc77bd..a162735ae 100755 --- a/projectq/cengines/_replacer/_decomposition_rule_test.py +++ b/projectq/cengines/_replacer/_decomposition_rule_test.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/cengines/_replacer/_replacer.py b/projectq/cengines/_replacer/_replacer.py index fbbf40c49..cc6745cd4 100755 --- a/projectq/cengines/_replacer/_replacer.py +++ b/projectq/cengines/_replacer/_replacer.py @@ -1,3 +1,5 @@ +# 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 @@ -126,34 +128,50 @@ def _process_command(self, cmd): # check for decomposition rules decomp_list = [] potential_decomps = [] - inv_list = [] - - # check for forward rules - cls = cmd.gate.__class__.__name__ - try: - potential_decomps = [ - d for d in self.decompositionRuleSet.decompositions[cls] - ] - except KeyError: - pass - # check for rules implementing the inverse gate - # and run them in reverse - inv_cls = get_inverse(cmd.gate).__class__.__name__ - try: - potential_decomps += [ - d.get_inverse_decomposition() - for d in self.decompositionRuleSet.decompositions[inv_cls] - ] - except KeyError: - pass - # throw out the ones which don't recognize the command - for d in potential_decomps: - if d.check(cmd): - decomp_list.append(d) + + # First check for a decomposition rules of the gate class, then + # the gate class of the inverse gate. If nothing is found, do the + # same for the first parent class, etc. + gate_mro = type(cmd.gate).mro()[:-1] + # If gate does not have an inverse it's parent classes are + # DaggeredGate, BasicGate, object. Hence don't check the last two + inverse_mro = type(get_inverse(cmd.gate)).mro()[:-2] + rules = self.decompositionRuleSet.decompositions + for level in range(max(len(gate_mro), len(inverse_mro))): + # Check for forward rules + if level < len(gate_mro): + class_name = gate_mro[level].__name__ + try: + potential_decomps = [d for d in rules[class_name]] + except KeyError: + pass + # throw out the ones which don't recognize the command + for d in potential_decomps: + if d.check(cmd): + decomp_list.append(d) + if len(decomp_list) != 0: + break + # Check for rules implementing the inverse gate + # and run them in reverse + if level < len(inverse_mro): + inv_class_name = inverse_mro[level].__name__ + try: + potential_decomps += [ + d.get_inverse_decomposition() + for d in rules[inv_class_name] + ] + except KeyError: + pass + # throw out the ones which don't recognize the command + for d in potential_decomps: + if d.check(cmd): + decomp_list.append(d) + if len(decomp_list) != 0: + break if len(decomp_list) == 0: - raise NoGateDecompositionError("\nNo replacement found for " - + str(cmd) + "!") + raise NoGateDecompositionError("\nNo replacement found for " + + str(cmd) + "!") # use decomposition chooser to determine the best decomposition chosen_decomp = self._decomp_chooser(cmd, decomp_list) diff --git a/projectq/cengines/_replacer/_replacer_test.py b/projectq/cengines/_replacer/_replacer_test.py index 43bb48763..f532cd998 100755 --- a/projectq/cengines/_replacer/_replacer_test.py +++ b/projectq/cengines/_replacer/_replacer_test.py @@ -1,3 +1,5 @@ +# 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 @@ -18,8 +20,8 @@ from projectq.cengines import (DummyEngine, DecompositionRuleSet, DecompositionRule) -from projectq.ops import H, X, Command, S, Rx, NotInvertible, Ry, BasicGate - +from projectq.ops import (BasicGate, ClassicalInstructionGate, Command, H, + NotInvertible, Rx, Ry, S, X) from projectq.cengines._replacer import _replacer @@ -39,18 +41,19 @@ def my_filter(self, cmd): assert not filter_eng.is_available(cmd2) -class TestGate(BasicGate): +class SomeGateClass(BasicGate): """ Test gate class """ + pass -TestGate = TestGate() +SomeGate = SomeGateClass() def make_decomposition_rule_set(): result = DecompositionRuleSet() # BasicGate with no get_inverse used for testing: with pytest.raises(NotInvertible): - TestGate.get_inverse() + SomeGate.get_inverse() # Loading of decomposition rules: def decompose_test1(cmd): @@ -61,7 +64,7 @@ def recognize_test(cmd): return True result.add_decomposition_rule( - DecompositionRule(TestGate.__class__, decompose_test1, + DecompositionRule(SomeGate.__class__, decompose_test1, recognize_test)) def decompose_test2(cmd): @@ -69,10 +72,10 @@ def decompose_test2(cmd): H | qb result.add_decomposition_rule( - DecompositionRule(TestGate.__class__, decompose_test2, + DecompositionRule(SomeGateClass, decompose_test2, recognize_test)) - assert len(result.decompositions[TestGate.__class__.__name__]) == 2 + assert len(result.decompositions[SomeGate.__class__.__name__]) == 2 return result rule_set = make_decomposition_rule_set() @@ -80,9 +83,9 @@ def decompose_test2(cmd): @pytest.fixture() def fixture_gate_filter(): - # Filter which doesn't allow TestGate + # Filter which doesn't allow SomeGate def test_gate_filter_func(self, cmd): - if cmd.gate == TestGate: + if cmd.gate == SomeGate: return False return True return _replacer.InstructionFilter(test_gate_filter_func) @@ -94,10 +97,10 @@ def test_auto_replacer_default_chooser(fixture_gate_filter): eng = MainEngine(backend=backend, engine_list=[_replacer.AutoReplacer(rule_set), fixture_gate_filter]) - assert len(rule_set.decompositions[TestGate.__class__.__name__]) == 2 + assert len(rule_set.decompositions[SomeGate.__class__.__name__]) == 2 assert len(backend.received_commands) == 0 qb = eng.allocate_qubit() - TestGate | qb + SomeGate | qb eng.flush() assert len(backend.received_commands) == 3 assert backend.received_commands[1].gate == X @@ -112,10 +115,10 @@ def test_decomp_chooser(cmd, decomposition_list): engine_list=[_replacer.AutoReplacer(rule_set, test_decomp_chooser), fixture_gate_filter]) - assert len(rule_set.decompositions[TestGate.__class__.__name__]) == 2 + assert len(rule_set.decompositions[SomeGate.__class__.__name__]) == 2 assert len(backend.received_commands) == 0 qb = eng.allocate_qubit() - TestGate | qb + SomeGate | qb eng.flush() assert len(backend.received_commands) == 3 assert backend.received_commands[1].gate == H @@ -188,10 +191,10 @@ def test_auto_replacer_adds_tags(fixture_gate_filter): eng = MainEngine(backend=backend, engine_list=[_replacer.AutoReplacer(rule_set), fixture_gate_filter]) - assert len(rule_set.decompositions[TestGate.__class__.__name__]) == 2 + assert len(rule_set.decompositions[SomeGate.__class__.__name__]) == 2 assert len(backend.received_commands) == 0 qb = eng.allocate_qubit() - cmd = Command(eng, TestGate, (qb,)) + cmd = Command(eng, SomeGate, (qb,)) cmd.tags = ["AddedTag"] eng.send([cmd]) eng.flush() @@ -199,3 +202,25 @@ def test_auto_replacer_adds_tags(fixture_gate_filter): assert backend.received_commands[1].gate == X assert len(backend.received_commands[1].tags) == 1 assert backend.received_commands[1].tags[0] == "AddedTag" + + +def test_auto_replacer_searches_parent_class_for_rule(): + class DerivedSomeGate(SomeGateClass): + pass + + def test_gate_filter_func(self, cmd): + if (cmd.gate == X or cmd.gate == H or + isinstance(cmd.gate, ClassicalInstructionGate)): + return True + return False + + i_filter = _replacer.InstructionFilter(test_gate_filter_func) + backend = DummyEngine(save_commands=True) + eng = MainEngine(backend=backend, + engine_list=[_replacer.AutoReplacer(rule_set), + i_filter]) + qb = eng.allocate_qubit() + DerivedSomeGate() | qb + eng.flush() + received_gate = backend.received_commands[1].gate + assert received_gate == X or received_gate == H diff --git a/projectq/cengines/_tagremover.py b/projectq/cengines/_tagremover.py index c73ca5e6e..bdfc5b86a 100755 --- a/projectq/cengines/_tagremover.py +++ b/projectq/cengines/_tagremover.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/cengines/_tagremover_test.py b/projectq/cengines/_tagremover_test.py index 706ad8803..1318b7bc9 100755 --- a/projectq/cengines/_tagremover_test.py +++ b/projectq/cengines/_tagremover_test.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/cengines/_testengine.py b/projectq/cengines/_testengine.py index d4e6263f6..cf992fcda 100755 --- a/projectq/cengines/_testengine.py +++ b/projectq/cengines/_testengine.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/cengines/_testengine_test.py b/projectq/cengines/_testengine_test.py index a9e7d8f7c..1a7d5bab8 100755 --- a/projectq/cengines/_testengine_test.py +++ b/projectq/cengines/_testengine_test.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/libs/__init__.py b/projectq/libs/__init__.py index 53f003f0a..ee1451dcd 100755 --- a/projectq/libs/__init__.py +++ b/projectq/libs/__init__.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/libs/math/__init__.py b/projectq/libs/math/__init__.py index 9fe921aea..1252fe007 100755 --- a/projectq/libs/math/__init__.py +++ b/projectq/libs/math/__init__.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/libs/math/_constantmath.py b/projectq/libs/math/_constantmath.py index bd72690bb..5bc106ba9 100755 --- a/projectq/libs/math/_constantmath.py +++ b/projectq/libs/math/_constantmath.py @@ -1,3 +1,5 @@ +# 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 @@ -11,7 +13,10 @@ # limitations under the License. import math -from fractions import gcd +try: + from math import gcd +except ImportError: + from fractions import gcd from projectq.ops import R, X, Swap, Measure, CNOT, QFT from projectq.meta import Control, Compute, Uncompute, CustomUncompute, Dagger diff --git a/projectq/libs/math/_constantmath_test.py b/projectq/libs/math/_constantmath_test.py index 17f9c08d6..7b26e3a53 100755 --- a/projectq/libs/math/_constantmath_test.py +++ b/projectq/libs/math/_constantmath_test.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/libs/math/_default_rules.py b/projectq/libs/math/_default_rules.py index 452370c21..af0cf979c 100755 --- a/projectq/libs/math/_default_rules.py +++ b/projectq/libs/math/_default_rules.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/libs/math/_gates.py b/projectq/libs/math/_gates.py index 8fa1860b8..7234afcac 100755 --- a/projectq/libs/math/_gates.py +++ b/projectq/libs/math/_gates.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/libs/math/_gates_test.py b/projectq/libs/math/_gates_test.py index 593296220..afc763a81 100755 --- a/projectq/libs/math/_gates_test.py +++ b/projectq/libs/math/_gates_test.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/meta/__init__.py b/projectq/meta/__init__.py index b167faec1..b0f26f036 100755 --- a/projectq/meta/__init__.py +++ b/projectq/meta/__init__.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/meta/_compute.py b/projectq/meta/_compute.py index 57a093d16..a070e1e8b 100755 --- a/projectq/meta/_compute.py +++ b/projectq/meta/_compute.py @@ -1,3 +1,5 @@ +# 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 @@ -192,32 +194,16 @@ def add_uncompute(command, old_tags=deepcopy(cmd.tags)): self.send([self._add_uncompute_tag(cmd.get_inverse())]) else: - if len(new_local_id) == 0: - # No local qubits are active currently -> do standard - # uncompute - self.send([self._add_uncompute_tag(cmd.get_inverse())]) - else: - # Process commands by replacing each local qubit from - # compute section with new local qubit from the uncompute - # section - tmp_control_qubits = cmd.control_qubits - changed = False - for control_qubit in tmp_control_qubits: - if control_qubit.id in new_local_id: - control_qubit.id = new_local_id[control_qubit.id] - changed = True - if changed: - cmd.control_qubits = tmp_control_qubits - tmp_qubits = cmd.qubits - changed = False - for qureg in tmp_qubits: + # Process commands by replacing each local qubit from + # compute section with new local qubit from the uncompute + # section + if new_local_id: # Only if we still have local qubits + for qureg in cmd.all_qubits: for qubit in qureg: if qubit.id in new_local_id: qubit.id = new_local_id[qubit.id] - changed = True - if changed: - cmd.qubits = tmp_qubits - self.send([self._add_uncompute_tag(cmd.get_inverse())]) + + self.send([self._add_uncompute_tag(cmd.get_inverse())]) def end_compute(self): """ diff --git a/projectq/meta/_compute_test.py b/projectq/meta/_compute_test.py index e02e6e424..3f9143861 100755 --- a/projectq/meta/_compute_test.py +++ b/projectq/meta/_compute_test.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/meta/_control.py b/projectq/meta/_control.py index 115ad35df..76ded4326 100755 --- a/projectq/meta/_control.py +++ b/projectq/meta/_control.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/meta/_control_test.py b/projectq/meta/_control_test.py index 43250522d..77bf538e4 100755 --- a/projectq/meta/_control_test.py +++ b/projectq/meta/_control_test.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/meta/_dagger.py b/projectq/meta/_dagger.py index 4f43bfced..333678ced 100755 --- a/projectq/meta/_dagger.py +++ b/projectq/meta/_dagger.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/meta/_dagger_test.py b/projectq/meta/_dagger_test.py index 8c46e9a9f..64a092c44 100755 --- a/projectq/meta/_dagger_test.py +++ b/projectq/meta/_dagger_test.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/meta/_dirtyqubit.py b/projectq/meta/_dirtyqubit.py index 7fe387479..e0693e8e6 100755 --- a/projectq/meta/_dirtyqubit.py +++ b/projectq/meta/_dirtyqubit.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/meta/_dirtyqubit_test.py b/projectq/meta/_dirtyqubit_test.py index 2c9010042..c23b49ba5 100755 --- a/projectq/meta/_dirtyqubit_test.py +++ b/projectq/meta/_dirtyqubit_test.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/meta/_loop.py b/projectq/meta/_loop.py index 429d05005..3371a9f1d 100755 --- a/projectq/meta/_loop.py +++ b/projectq/meta/_loop.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/meta/_loop_test.py b/projectq/meta/_loop_test.py index b43664355..2755a690c 100755 --- a/projectq/meta/_loop_test.py +++ b/projectq/meta/_loop_test.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/meta/_qubitplacement.py b/projectq/meta/_qubitplacement.py index 96bbbd094..32bf01bd4 100755 --- a/projectq/meta/_qubitplacement.py +++ b/projectq/meta/_qubitplacement.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/meta/_qubitplacement_test.py b/projectq/meta/_qubitplacement_test.py index 5ab1debd2..003212853 100755 --- a/projectq/meta/_qubitplacement_test.py +++ b/projectq/meta/_qubitplacement_test.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/meta/_util.py b/projectq/meta/_util.py index 9381f94a1..856ef6728 100755 --- a/projectq/meta/_util.py +++ b/projectq/meta/_util.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/meta/_util_test.py b/projectq/meta/_util_test.py index 7aad8be2f..2b4ec892b 100755 --- a/projectq/meta/_util_test.py +++ b/projectq/meta/_util_test.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/ops/__init__.py b/projectq/ops/__init__.py index 0c798929c..f325d3f04 100755 --- a/projectq/ops/__init__.py +++ b/projectq/ops/__init__.py @@ -1,3 +1,5 @@ +# 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 @@ -17,7 +19,8 @@ BasicRotationGate, ClassicalInstructionGate, FastForwardingGate, - BasicMathGate) + BasicMathGate, + BasicPhaseGate) from ._command import apply_command, Command from ._metagates import (DaggeredGate, get_inverse, diff --git a/projectq/ops/_basics.py b/projectq/ops/_basics.py index c64021ee2..4d1d0226f 100755 --- a/projectq/ops/_basics.py +++ b/projectq/ops/_basics.py @@ -1,3 +1,5 @@ +# 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 @@ -310,6 +312,95 @@ def __ne__(self, other): return not self.__eq__(other) +class BasicPhaseGate(BasicGate): + """ + Defines a base class of a phase gate. + + A phase gate has a continuous parameter (the angle), labeled 'angle' / + self._angle. Its inverse is the same gate with the negated argument. + Phase gates of the same class can be merged by adding the angles. + The continuous parameter is modulo 2 * pi, self._angle is in the interval + [0, 2 * pi). + """ + def __init__(self, angle): + """ + Initialize a basic rotation gate. + + Args: + angle (float): Angle of rotation (saved modulo 2 * pi) + """ + BasicGate.__init__(self) + self._angle = float(angle) % (2. * math.pi) + + def __str__(self): + """ + Return the string representation of a BasicRotationGate. + + Returns the class name and the angle as + + .. code-block:: python + + [CLASSNAME]([ANGLE]) + """ + return str(self.__class__.__name__) + "(" + str(self._angle) + ")" + + def tex_str(self): + """ + Return the Latex string representation of a BasicRotationGate. + + Returns the class name and the angle as a subscript, i.e. + + .. code-block:: latex + + [CLASSNAME]$_[ANGLE]$ + """ + return str(self.__class__.__name__) + "$_{" + str(self._angle) + "}$" + + def get_inverse(self): + """ + Return the inverse of this rotation gate (negate the angle, return new + object). + """ + if self._angle == 0: + return self.__class__(0) + else: + return self.__class__(-self._angle + 2 * math.pi) + + def get_merged(self, other): + """ + Return self merged with another gate. + + Default implementation handles rotation gate of the same type, where + angles are simply added. + + Args: + other: Rotation gate of same type. + + Raises: + NotMergeable: For non-rotation gates or rotation gates of + different type. + + Returns: + New object representing the merged gates. + """ + if isinstance(other, self.__class__): + return self.__class__(self._angle + other._angle) + raise NotMergeable("Can't merge different types of rotation gates.") + + def __eq__(self, other): + """ Return True if same class and same rotation angle. """ + tolerance = EQ_TOLERANCE + if isinstance(other, self.__class__): + difference = abs(self._angle - other._angle) % (2 * math.pi) + # Return True if angles are close to each other modulo 4 * pi + if difference < tolerance or difference > 2 * math.pi - tolerance: + return True + return False + + def __ne__(self, other): + return not self.__eq__(other) + + # Classical instruction gates never have control qubits. class ClassicalInstructionGate(BasicGate): """ diff --git a/projectq/ops/_basics_test.py b/projectq/ops/_basics_test.py index b50e37ddb..423895501 100755 --- a/projectq/ops/_basics_test.py +++ b/projectq/ops/_basics_test.py @@ -1,3 +1,5 @@ +# 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 @@ -189,6 +191,64 @@ def test_basic_rotation_gate_comparison(): assert basic_rotation_gate2 != _basics.BasicRotationGate(0.5 + 2 * math.pi) +@pytest.mark.parametrize("input_angle, modulo_angle", + [(2.0, 2.0), (17., 4.4336293856408275), + (-0.5 * math.pi, 1.5 * math.pi), (2 * math.pi, 0)]) +def test_basic_phase_gate_init(input_angle, modulo_angle): + # Test internal representation + gate = _basics.BasicPhaseGate(input_angle) + assert gate._angle == pytest.approx(modulo_angle) + + +def test_basic_phase_gate_str(): + basic_phase_gate = _basics.BasicPhaseGate(0.5) + assert str(basic_phase_gate) == "BasicPhaseGate(0.5)" + + +def test_basic_phase_tex_str(): + basic_phase_gate = _basics.BasicPhaseGate(0.5) + assert basic_phase_gate.tex_str() == "BasicPhaseGate$_{0.5}$" + + +@pytest.mark.parametrize("input_angle, inverse_angle", + [(2.0, -2.0 + 2 * math.pi), (-0.5, 0.5), (0.0, 0)]) +def test_basic_phase_gate_get_inverse(input_angle, inverse_angle): + basic_phase_gate = _basics.BasicPhaseGate(input_angle) + inverse = basic_phase_gate.get_inverse() + assert isinstance(inverse, _basics.BasicPhaseGate) + assert inverse._angle == pytest.approx(inverse_angle) + + +def test_basic_phase_gate_get_merged(): + basic_gate = _basics.BasicGate() + basic_phase_gate1 = _basics.BasicPhaseGate(0.5) + basic_phase_gate2 = _basics.BasicPhaseGate(1.0) + basic_phase_gate3 = _basics.BasicPhaseGate(1.5) + with pytest.raises(_basics.NotMergeable): + basic_phase_gate1.get_merged(basic_gate) + merged_gate = basic_phase_gate1.get_merged(basic_phase_gate2) + assert merged_gate == basic_phase_gate3 + + +def test_basic_phase_gate_comparison(): + basic_phase_gate1 = _basics.BasicPhaseGate(0.5) + basic_phase_gate2 = _basics.BasicPhaseGate(0.5) + basic_phase_gate3 = _basics.BasicPhaseGate(0.5 + 2 * math.pi) + assert basic_phase_gate1 == basic_phase_gate2 + assert basic_phase_gate1 == basic_phase_gate3 + basic_phase_gate4 = _basics.BasicPhaseGate(0.50000001) + # Test __ne__: + assert basic_phase_gate4 != basic_phase_gate1 + # Test one gate close to 2*pi the other one close to 0 + basic_phase_gate5 = _basics.BasicPhaseGate(1.e-13) + basic_phase_gate6 = _basics.BasicPhaseGate(2 * math.pi - 1.e-13) + assert basic_phase_gate5 == basic_phase_gate6 + # Test different types of gates + basic_gate = _basics.BasicGate() + assert not basic_gate == basic_phase_gate6 + assert basic_phase_gate2 != _basics.BasicPhaseGate(0.5 + math.pi) + + def test_basic_math_gate(): def my_math_function(a, b, c): return (a, b, c + a * b) diff --git a/projectq/ops/_command.py b/projectq/ops/_command.py index 7745c6049..5186502fa 100755 --- a/projectq/ops/_command.py +++ b/projectq/ops/_command.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/ops/_command_test.py b/projectq/ops/_command_test.py index 8a5bf0029..ae1407836 100755 --- a/projectq/ops/_command_test.py +++ b/projectq/ops/_command_test.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/ops/_gates.py b/projectq/ops/_gates.py index 08f0863bb..ee691175b 100755 --- a/projectq/ops/_gates.py +++ b/projectq/ops/_gates.py @@ -1,3 +1,5 @@ +# 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 @@ -35,6 +37,7 @@ from ._basics import (BasicGate, SelfInverseGate, BasicRotationGate, + BasicPhaseGate, ClassicalInstructionGate, FastForwardingGate, BasicMathGate) @@ -145,7 +148,7 @@ def __str__(self): Entangle = EntangleGate() -class Ph(BasicRotationGate): +class Ph(BasicPhaseGate): """ Phase gate (global phase) """ @property def matrix(self): @@ -181,7 +184,7 @@ def matrix(self): [0, cmath.exp(.5 * 1j * self._angle)]]) -class R(BasicRotationGate): +class R(BasicPhaseGate): """ Phase-shift gate (equivalent to Rz up to a global phase) """ @property def matrix(self): diff --git a/projectq/ops/_gates_test.py b/projectq/ops/_gates_test.py index de43b6bf7..b70707845 100755 --- a/projectq/ops/_gates_test.py +++ b/projectq/ops/_gates_test.py @@ -1,3 +1,5 @@ +# 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 @@ -129,6 +131,31 @@ def test_rz(angle): assert np.allclose(gate.matrix, expected_matrix) +@pytest.mark.parametrize("angle", [0, 0.2, 2.1, 4.1, 2 * math.pi]) +def test_ph(angle): + gate = _gates.Ph(angle) + gate2 = _gates.Ph(angle + 2 * math.pi) + expected_matrix = np.matrix([[cmath.exp(1j * angle), 0], + [0, cmath.exp(1j * angle)]]) + assert gate.matrix.shape == expected_matrix.shape + assert np.allclose(gate.matrix, expected_matrix) + assert gate2.matrix.shape == expected_matrix.shape + assert np.allclose(gate2.matrix, expected_matrix) + assert gate == gate2 + + +@pytest.mark.parametrize("angle", [0, 0.2, 2.1, 4.1, 2 * math.pi]) +def test_r(angle): + gate = _gates.R(angle) + gate2 = _gates.R(angle + 2 * math.pi) + expected_matrix = np.matrix([[1, 0], [0, cmath.exp(1j * angle)]]) + assert gate.matrix.shape == expected_matrix.shape + assert np.allclose(gate.matrix, expected_matrix) + assert gate2.matrix.shape == expected_matrix.shape + assert np.allclose(gate2.matrix, expected_matrix) + assert gate == gate2 + + def test_flush_gate(): gate = _gates.FlushGate() assert str(gate) == "" diff --git a/projectq/ops/_metagates.py b/projectq/ops/_metagates.py index 9b61c5cef..002e07e2a 100755 --- a/projectq/ops/_metagates.py +++ b/projectq/ops/_metagates.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/ops/_metagates_test.py b/projectq/ops/_metagates_test.py index 3baab224c..9c1656a6c 100755 --- a/projectq/ops/_metagates_test.py +++ b/projectq/ops/_metagates_test.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/ops/_qftgate.py b/projectq/ops/_qftgate.py index f2c70341e..c2876d67d 100755 --- a/projectq/ops/_qftgate.py +++ b/projectq/ops/_qftgate.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/ops/_qftgate_test.py b/projectq/ops/_qftgate_test.py index 439f09483..a74683006 100755 --- a/projectq/ops/_qftgate_test.py +++ b/projectq/ops/_qftgate_test.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/ops/_qubit_operator.py b/projectq/ops/_qubit_operator.py index c7da39406..22795013f 100644 --- a/projectq/ops/_qubit_operator.py +++ b/projectq/ops/_qubit_operator.py @@ -1,3 +1,5 @@ +# 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 @@ -124,7 +126,7 @@ def __init__(self, term=None, coefficient=1.): be sorted by the qubit number. '' is the identity. Raises: - QubitOperatorError: Invalid operators provided to QubitOperator. + QubitOperatorError: Invalid operators provided to QubitOperator. """ if not isinstance(coefficient, (int, float, complex)): raise ValueError('Coefficient must be a numeric type.') @@ -229,7 +231,7 @@ def __imul__(self, multiplier): In-place multiply (*=) terms with scalar or QubitOperator. Args: - multiplier(complex float, or QubitOperator): multiplier + multiplier(complex float, or QubitOperator): multiplier """ # Handle scalars. if isinstance(multiplier, (int, float, complex)): @@ -305,13 +307,13 @@ def __mul__(self, multiplier): Return self * multiplier for a scalar, or a QubitOperator. Args: - multiplier: A scalar, or a QubitOperator. + multiplier: A scalar, or a QubitOperator. Returns: - product: A QubitOperator. + product: A QubitOperator. Raises: - TypeError: Invalid type cannot be multiply with QubitOperator. + TypeError: Invalid type cannot be multiply with QubitOperator. """ if (isinstance(multiplier, (int, float, complex)) or isinstance(multiplier, QubitOperator)): @@ -331,13 +333,13 @@ def __rmul__(self, multiplier): is also queried as the default behavior. Args: - multiplier: A scalar to multiply by. + multiplier: A scalar to multiply by. Returns: - product: A new instance of QubitOperator. + product: A new instance of QubitOperator. Raises: - TypeError: Object of invalid type cannot multiply QubitOperator. + TypeError: Object of invalid type cannot multiply QubitOperator. """ if not isinstance(multiplier, (int, float, complex)): raise TypeError( @@ -352,14 +354,13 @@ def __truediv__(self, divisor): This is always floating point division. Args: - divisor: A scalar to divide by. + divisor: A scalar to divide by. Returns: - A new instance of QubitOperator. + A new instance of QubitOperator. Raises: - TypeError: Cannot divide local operator by non-scalar type. - + TypeError: Cannot divide local operator by non-scalar type. """ if not isinstance(divisor, (int, float, complex)): raise TypeError('Cannot divide QubitOperator by non-scalar type.') @@ -384,10 +385,10 @@ def __iadd__(self, addend): In-place method for += addition of QubitOperator. Args: - addend: A QubitOperator. + addend: A QubitOperator. Raises: - TypeError: Cannot add invalid type. + TypeError: Cannot add invalid type. """ if isinstance(addend, QubitOperator): for term in addend.terms: @@ -408,33 +409,34 @@ def __add__(self, addend): summand += addend return summand - def __sub__(self, subtrahend): - """ - Return self - subtrahend for a QubitOperator. - - Args: - addend: A QubitOperator. - - Raises: - TypeError: Cannot add invalid type. - """ - if not isinstance(subtrahend, QubitOperator): - raise TypeError('Cannot subtract invalid type to QubitOperator.') - return self + (-1. * subtrahend) - def __isub__(self, subtrahend): """ - In-place method for -= addition of QubitOperator. + In-place method for -= subtraction of QubitOperator. Args: - subtrahend: A QubitOperator. + subtrahend: A QubitOperator. Raises: - TypeError: Cannot add invalid type. + TypeError: Cannot subtract invalid type from QubitOperator. """ - if not isinstance(subtrahend, QubitOperator): - raise TypeError('Cannot subtract invalid type to QubitOperator.') - return self.__iadd__(-1. * subtrahend) + if isinstance(subtrahend, QubitOperator): + for term in subtrahend.terms: + if term in self.terms: + if abs(self.terms[term] - subtrahend.terms[term]) > 0.: + self.terms[term] -= subtrahend.terms[term] + else: + del self.terms[term] + else: + self.terms[term] = -subtrahend.terms[term] + else: + raise TypeError('Cannot subtract invalid type from QubitOperator.') + return self + + def __sub__(self, subtrahend): + """ Return self - subtrahend for a QubitOperator. """ + minuend = copy.deepcopy(self) + minuend -= subtrahend + return minuend def __neg__(self): return -1. * self diff --git a/projectq/ops/_qubit_operator_test.py b/projectq/ops/_qubit_operator_test.py index f40768608..f7f76cd61 100644 --- a/projectq/ops/_qubit_operator_test.py +++ b/projectq/ops/_qubit_operator_test.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/ops/_shortcuts.py b/projectq/ops/_shortcuts.py index 409731c26..a5a765282 100755 --- a/projectq/ops/_shortcuts.py +++ b/projectq/ops/_shortcuts.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/ops/_shortcuts_test.py b/projectq/ops/_shortcuts_test.py index a5c595a43..d6bd8b707 100755 --- a/projectq/ops/_shortcuts_test.py +++ b/projectq/ops/_shortcuts_test.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/ops/_time_evolution.py b/projectq/ops/_time_evolution.py index e0cab0fee..46e8979b6 100644 --- a/projectq/ops/_time_evolution.py +++ b/projectq/ops/_time_evolution.py @@ -1,3 +1,5 @@ +# 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 @@ -35,7 +37,7 @@ class TimeEvolution(BasicGate): Example: .. code-block:: python - wavefuction = eng.allocate_qureg(5) + wavefunction = eng.allocate_qureg(5) hamiltonian = 0.5 * QubitOperator("X0 Z1 Y5") # Apply exp(-i * H * t) to the wavefunction: TimeEvolution(time=2.0, hamiltonian=hamiltonian) | wavefunction @@ -161,7 +163,7 @@ def __or__(self, qubits): While in the above example the TimeEvolution gate is applied to 5 qubits, the hamiltonian of this TimeEvolution gate acts only - non-trivially on the two qubits wavefuction[1] and wavefunction[3]. + non-trivially on the two qubits wavefunction[1] and wavefunction[3]. Therefore, the operator| will rescale the indices in the hamiltonian and sends the equivalent of the following new gate to the MainEngine: diff --git a/projectq/ops/_time_evolution_test.py b/projectq/ops/_time_evolution_test.py index 0950a4fc5..cbad44fa2 100644 --- a/projectq/ops/_time_evolution_test.py +++ b/projectq/ops/_time_evolution_test.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/setups/__init__.py b/projectq/setups/__init__.py index 53f003f0a..ee1451dcd 100755 --- a/projectq/setups/__init__.py +++ b/projectq/setups/__init__.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/setups/decompositions/__init__.py b/projectq/setups/decompositions/__init__.py index cd6b00116..26d08334e 100755 --- a/projectq/setups/decompositions/__init__.py +++ b/projectq/setups/decompositions/__init__.py @@ -1,3 +1,5 @@ +# 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 @@ -10,7 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -from . import (crz2cxandrz, +from . import (arb1qubit2rzandry, + carb1qubit2cnotrzandry, + crz2cxandrz, + cnu2toffoliandcu, entangle, globalphase, ph2r, @@ -22,7 +27,10 @@ all_defined_decomposition_rules = [ rule - for module in [crz2cxandrz, + for module in [arb1qubit2rzandry, + carb1qubit2cnotrzandry, + crz2cxandrz, + cnu2toffoliandcu, entangle, globalphase, ph2r, diff --git a/projectq/setups/decompositions/_gates_test.py b/projectq/setups/decompositions/_gates_test.py index c6a10df04..54324cf90 100755 --- a/projectq/setups/decompositions/_gates_test.py +++ b/projectq/setups/decompositions/_gates_test.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/setups/decompositions/arb1qubit2rzandry.py b/projectq/setups/decompositions/arb1qubit2rzandry.py new file mode 100644 index 000000000..8b1fb5a23 --- /dev/null +++ b/projectq/setups/decompositions/arb1qubit2rzandry.py @@ -0,0 +1,218 @@ +# 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. + +""" +Registers the Z-Y decomposition for an arbitrary one qubit gate. + +See paper "Elementary gates for quantum computing" by Adriano Barenco et al., +arXiv:quant-ph/9503016v1. (Note: They use different gate definitions!) +Or see theorem 4.1 in Nielsen and Chuang. + +Decompose an arbitrary one qubit gate U into +U = e^(i alpha) Rz(beta) Ry(gamma) Rz(delta). If a gate V is element of SU(2), +i.e., determinant == 1, then +V = Rz(beta) Ry(gamma) Rz(delta) + +""" + +import cmath +import itertools +import math + + +import numpy + +from projectq.cengines import DecompositionRule +from projectq.meta import Control +from projectq.ops import BasicGate, Ph, Ry, Rz + + +TOLERANCE = 1e-12 + + +def _recognize_arb1qubit(cmd): + """ Recognize an arbitrary one qubit gate which has a matrix property.""" + try: + m = cmd.gate.matrix + if len(m) == 2: + return True + else: + return False + except: + return False + + +def _test_parameters(matrix, a, b_half, c_half, d_half): + """ + It builds matrix U with parameters (a, b/2, c/2, d/2) and compares against + matrix. + + U = [[exp(j*(a-b/2-d/2))*cos(c/2), -exp(j*(a-b/2+d/2))*sin(c/2)], + [exp(j*(a+b/2-d/2))*sin(c/2), exp(j*(a+b/2+d/2))*cos(c/2)]] + + Args: + matrix(list): 2x2 matrix + a: parameter of U + b_half: b/2. parameter of U + c_half: c/2. parameter of U + d_half: d/2. parameter of U + + Returns: + True if matrix elements of U and `matrix` are TOLERANCE close. + """ + U = [[cmath.exp(1j*(a-b_half-d_half))*math.cos(c_half), + -cmath.exp(1j*(a-b_half+d_half))*math.sin(c_half)], + [cmath.exp(1j*(a+b_half-d_half))*math.sin(c_half), + cmath.exp(1j*(a+b_half+d_half))*math.cos(c_half)]] + return numpy.allclose(U, matrix, rtol=10*TOLERANCE, atol=TOLERANCE) + + +def _find_parameters(matrix): + """ + Given a 2x2 unitary matrix, find the parameters + a, b/2, c/2, and d/2 such that + matrix == [[exp(j*(a-b/2-d/2))*cos(c/2), -exp(j*(a-b/2+d/2))*sin(c/2)], + [exp(j*(a+b/2-d/2))*sin(c/2), exp(j*(a+b/2+d/2))*cos(c/2)]] + + Note: + If the matrix is element of SU(2) (determinant == 1), then + we can choose a = 0. + + Args: + matrix(list): 2x2 unitary matrix + + Returns: + parameters of the matrix: (a, b/2, c/2, d/2) + """ + # Determine a, b/2, c/2 and d/2 (3 different cases). + # Note: everything is modulo 2pi. + # Case 1: sin(c/2) == 0: + if abs(matrix[0][1]) < TOLERANCE: + two_a = cmath.phase(matrix[0][0]*matrix[1][1]) % (2*math.pi) + if abs(two_a) < TOLERANCE or abs(two_a) > 2*math.pi-TOLERANCE: + # from 2a==0 (mod 2pi), it follows that a==0 or a==pi, + # w.l.g. we can choose a==0 because (see U above) + # c/2 -> c/2 + pi would have the same effect as as a==0 -> a==pi. + a = 0 + else: + a = two_a/2. + d_half = 0 # w.l.g + b = cmath.phase(matrix[1][1])-cmath.phase(matrix[0][0]) + possible_b_half = [(b/2.) % (2*math.pi), (b/2.+math.pi) % (2*math.pi)] + # As we have fixed a, we need to find correct sign for cos(c/2) + possible_c_half = [0.0, math.pi] + found = False + for b_half, c_half in itertools.product(possible_b_half, + possible_c_half): + if _test_parameters(matrix, a, b_half, c_half, d_half): + found = True + break + if not found: + raise Exception("Couldn't find parameters for matrix ", matrix, + "This shouldn't happen. Maybe the matrix is " + + "not unitary?") + # Case 2: cos(c/2) == 0: + elif abs(matrix[0][0]) < TOLERANCE: + two_a = cmath.phase(-matrix[0][1]*matrix[1][0]) % (2*math.pi) + if abs(two_a) < TOLERANCE or abs(two_a) > 2*math.pi-TOLERANCE: + # from 2a==0 (mod 2pi), it follows that a==0 or a==pi, + # w.l.g. we can choose a==0 because (see U above) + # c/2 -> c/2 + pi would have the same effect as as a==0 -> a==pi. + a = 0 + else: + a = two_a/2. + d_half = 0 # w.l.g + b = cmath.phase(matrix[1][0])-cmath.phase(matrix[0][1]) + math.pi + possible_b_half = [(b/2.) % (2*math.pi), (b/2.+math.pi) % (2*math.pi)] + # As we have fixed a, we need to find correct sign for sin(c/2) + possible_c_half = [math.pi/2., 3./2.*math.pi] + found = False + for b_half, c_half in itertools.product(possible_b_half, + possible_c_half): + if _test_parameters(matrix, a, b_half, c_half, d_half): + found = True + break + if not found: + raise Exception("Couldn't find parameters for matrix ", matrix, + "This shouldn't happen. Maybe the matrix is " + + "not unitary?") + # Case 3: sin(c/2) != 0 and cos(c/2) !=0: + else: + two_a = cmath.phase(matrix[0][0]*matrix[1][1]) % (2*math.pi) + if abs(two_a) < TOLERANCE or abs(two_a) > 2*math.pi-TOLERANCE: + # from 2a==0 (mod 2pi), it follows that a==0 or a==pi, + # w.l.g. we can choose a==0 because (see U above) + # c/2 -> c/2 + pi would have the same effect as as a==0 -> a==pi. + a = 0 + else: + a = two_a/2. + two_d = 2.*cmath.phase(matrix[0][1])-2.*cmath.phase(matrix[0][0]) + possible_d_half = [two_d/4. % (2*math.pi), + (two_d/4.+math.pi/2.) % (2*math.pi), + (two_d/4.+math.pi) % (2*math.pi), + (two_d/4.+3./2.*math.pi) % (2*math.pi)] + two_b = 2.*cmath.phase(matrix[1][0])-2.*cmath.phase(matrix[0][0]) + possible_b_half = [two_b/4. % (2*math.pi), + (two_b/4.+math.pi/2.) % (2*math.pi), + (two_b/4.+math.pi) % (2*math.pi), + (two_b/4.+3./2.*math.pi) % (2*math.pi)] + tmp = math.acos(abs(matrix[1][1])) + possible_c_half = [tmp % (2*math.pi), + (tmp+math.pi) % (2*math.pi), + (-1.*tmp) % (2*math.pi), + (-1.*tmp+math.pi) % (2*math.pi)] + found = False + for b_half, c_half, d_half in itertools.product(possible_b_half, + possible_c_half, + possible_d_half): + if _test_parameters(matrix, a, b_half, c_half, d_half): + found = True + break + if not found: + raise Exception("Couldn't find parameters for matrix ", matrix, + "This shouldn't happen. Maybe the matrix is " + + "not unitary?") + return (a, b_half, c_half, d_half) + + +def _decompose_arb1qubit(cmd): + """ + Use Z-Y decomposition of Nielsen and Chuang (Theorem 4.1). + + An arbitrary one qubit gate matrix can be writen as + U = [[exp(j*(a-b/2-d/2))*cos(c/2), -exp(j*(a-b/2+d/2))*sin(c/2)], + [exp(j*(a+b/2-d/2))*sin(c/2), exp(j*(a+b/2+d/2))*cos(c/2)]] + where a,b,c,d are real numbers. + Then U = exp(j*a) Rz(b) Ry(c) Rz(d). + If the matrix is element of SU(2) (determinant == 1), then + we can choose a = 0. + """ + matrix = cmd.gate.matrix.tolist() + a, b_half, c_half, d_half = _find_parameters(matrix) + qb = cmd.qubits + eng = cmd.engine + with Control(eng, cmd.control_qubits): + if Rz(2*d_half) != Rz(0): + Rz(2*d_half) | qb + if Ry(2*c_half) != Ry(0): + Ry(2*c_half) | qb + if Rz(2*b_half) != Rz(0): + Rz(2*b_half) | qb + if a != 0: + Ph(a) | qb + + +all_defined_decomposition_rules = [ + DecompositionRule(BasicGate, _decompose_arb1qubit, _recognize_arb1qubit) +] diff --git a/projectq/setups/decompositions/arb1qubit2rzandry_test.py b/projectq/setups/decompositions/arb1qubit2rzandry_test.py new file mode 100644 index 000000000..e52728ea3 --- /dev/null +++ b/projectq/setups/decompositions/arb1qubit2rzandry_test.py @@ -0,0 +1,171 @@ +# 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 projectq.setups.decompositions.arb1qubit2rzandry.py." + +from cmath import exp +import math + +import numpy as np +import pytest + +from projectq.backends import Simulator +from projectq.cengines import (AutoReplacer, DecompositionRuleSet, + DummyEngine, InstructionFilter, MainEngine) +from projectq.ops import (BasicGate, ClassicalInstructionGate, Measure, Ph, R, + Rx, Ry, Rz, X) + +from . import arb1qubit2rzandry as arb1q + + +def test_recognize_correct_gates(): + saving_backend = DummyEngine(save_commands=True) + eng = MainEngine(backend=saving_backend) + qubit = eng.allocate_qubit() + Ph(0.1) | qubit + R(0.2) | qubit + Rx(0.3) | qubit + X | qubit + eng.flush(deallocate_qubits=True) + # Don't test initial allocate and trailing deallocate and flush gate. + for cmd in saving_backend.received_commands[1:-2]: + assert arb1q._recognize_arb1qubit(cmd) + + +def test_recognize_incorrect_gates(): + saving_backend = DummyEngine(save_commands=True) + eng = MainEngine(backend=saving_backend) + qubit = eng.allocate_qubit() + # Does not have matrix attribute: + BasicGate() | qubit + # Two qubit gate: + two_qubit_gate = BasicGate() + two_qubit_gate.matrix = [[1, 0, 0, 0], [0, 1, 0, 0], + [0, 0, 1, 0], [0, 0, 0, 1]] + two_qubit_gate | qubit + eng.flush(deallocate_qubits=True) + for cmd in saving_backend.received_commands: + assert not arb1q._recognize_arb1qubit(cmd) + + +def z_y_decomp_gates(eng, cmd): + g = cmd.gate + if isinstance(g, ClassicalInstructionGate): + return True + if len(cmd.control_qubits) == 0: + if (isinstance(cmd.gate, Ry) or + isinstance(cmd.gate, Rz) or + isinstance(cmd.gate, Ph)): + return True + return False + + +def create_unitary_matrix(a, b, c, d): + """ + Creates a unitary 2x2 matrix given parameters. + + Any unitary 2x2 matrix can be parametrized by: + U = exp(ia) [[exp(j*b) * cos(d), exp(j*c) * sin(d)], + [-exp(-j*c) * sin(d), exp(-j*b) * cos(d)]] + with 0 <= d <= pi/2 and 0 <= a,b,c < 2pi. If a==0, then + det(U) == 1 and hence U is element of SU(2). + + Args: + a,b,c (float): parameters 0 <= a,b,c < 2pi + d (float): parameter 0 <= d <= pi/2 + + Returns: + 2x2 matrix as nested lists + """ + ph = exp(1j*a) # global phase + return [[ph * exp(1j*b) * math.cos(d), ph * exp(1j*c) * math.sin(d)], + [ph * -exp(-1j*c) * math.sin(d), ph * exp(-1j*b) * math.cos(d)]] + + +def create_test_matrices(): + params = [(0.2, 0.3, 0.5, math.pi * 0.4), + (1e-14, 0.3, 0.5, 0), + (0.4, 0.0, math.pi * 2, 0.7), + (0.0, 0.2, math.pi * 1.2, 1.5), # element of SU(2) + (0.4, 0.0, math.pi * 1.3, 0.8), + (0.4, 4.1, math.pi * 1.3, 0), + (5.1, 1.2, math.pi * 1.5, math.pi/2.), + (1e-13, 1.2, math.pi * 3.7, math.pi/2.), + (0, math.pi/2., 0, 0), + (math.pi/2., -math.pi/2., 0, 0), + (math.pi/2., math.pi/2., 0.1, 0.4), + (math.pi*1.5, math.pi/2., 0, 0.4)] + matrices = [] + for a, b, c, d in params: + matrices.append(create_unitary_matrix(a, b, c, d)) + return matrices + + +@pytest.mark.parametrize("gate_matrix", create_test_matrices()) +def test_decomposition(gate_matrix): + for basis_state in ([1, 0], [0, 1]): + # Create single qubit gate with gate_matrix + test_gate = BasicGate() + test_gate.matrix = np.matrix(gate_matrix) + + correct_dummy_eng = DummyEngine(save_commands=True) + correct_eng = MainEngine(backend=Simulator(), + engine_list=[correct_dummy_eng]) + + rule_set = DecompositionRuleSet(modules=[arb1q]) + test_dummy_eng = DummyEngine(save_commands=True) + test_eng = MainEngine(backend=Simulator(), + engine_list=[AutoReplacer(rule_set), + InstructionFilter(z_y_decomp_gates), + test_dummy_eng]) + + correct_qb = correct_eng.allocate_qubit() + correct_eng.flush() + test_qb = test_eng.allocate_qubit() + test_eng.flush() + + correct_eng.backend.set_wavefunction(basis_state, correct_qb) + test_eng.backend.set_wavefunction(basis_state, test_qb) + + test_gate | test_qb + test_gate | correct_qb + + test_eng.flush() + correct_eng.flush() + + assert correct_dummy_eng.received_commands[2].gate == test_gate + assert test_dummy_eng.received_commands[2].gate != test_gate + + for fstate in ['0', '1']: + test = test_eng.backend.get_amplitude(fstate, test_qb) + correct = correct_eng.backend.get_amplitude(fstate, correct_qb) + assert correct == pytest.approx(test, rel=1e-12, abs=1e-12) + + Measure | test_qb + Measure | correct_qb + + +@pytest.mark.parametrize("gate_matrix", [[[2, 0], [0, 4]], + [[0, 2], [4, 0]], + [[1, 2], [4, 0]]]) +def test_decomposition_errors(gate_matrix): + test_gate = BasicGate() + test_gate.matrix = np.matrix(gate_matrix) + rule_set = DecompositionRuleSet(modules=[arb1q]) + eng = MainEngine(backend=DummyEngine(), + engine_list=[AutoReplacer(rule_set), + InstructionFilter(z_y_decomp_gates)]) + qb = eng.allocate_qubit() + with pytest.raises(Exception): + test_gate | qb diff --git a/projectq/setups/decompositions/carb1qubit2cnotrzandry.py b/projectq/setups/decompositions/carb1qubit2cnotrzandry.py new file mode 100644 index 000000000..e133f89d6 --- /dev/null +++ b/projectq/setups/decompositions/carb1qubit2cnotrzandry.py @@ -0,0 +1,228 @@ +# 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. + +""" +Registers the decomposition of an controlled arbitary single qubit gate. + +See paper "Elementary gates for quantum computing" by Adriano Barenco et al., +arXiv:quant-ph/9503016v1. (Note: They use different gate definitions!) or +Nielsen and Chuang chapter 4.3. +""" + +import cmath +import itertools +import math + +import numpy + +from projectq.cengines import DecompositionRule +from projectq.meta import get_control_count, Control +from projectq.ops import BasicGate, CNOT, Ph, Ry, Rz, X +from projectq.setups.decompositions import arb1qubit2rzandry as arb1q + + +TOLERANCE = 1e-12 + + +def _recognize_carb1qubit(cmd): + """ Recognize single controlled one qubit gates with a matrix.""" + if get_control_count(cmd) == 1: + try: + m = cmd.gate.matrix + if len(m) == 2: + return True + except: + return False + return False + + +def _test_parameters(matrix, a, b, c_half): + """ + It builds matrix V with parameters (a, b, c/2) and compares against + matrix. + + V = [[-sin(c/2) * exp(j*a), exp(j*(a-b)) * cos(c/2)], + [exp(j*(a+b)) * cos(c/2), exp(j*a) * sin(c/2)]] + + Args: + matrix(list): 2x2 matrix + a: Parameter of V + b: Parameter of V + c_half: c/2. Parameter of V + + Returns: + True if matrix elements of V and `matrix` are TOLERANCE close. + """ + V = [[-math.sin(c_half)*cmath.exp(1j*a), + cmath.exp(1j*(a-b))*math.cos(c_half)], + [cmath.exp(1j*(a+b))*math.cos(c_half), + cmath.exp(1j*a) * math.sin(c_half)]] + return numpy.allclose(V, matrix, rtol=10*TOLERANCE, atol=TOLERANCE) + + +def _recognize_v(matrix): + """ + Recognizes a matrix which can be written in the following form: + + V = [[-sin(c/2) * exp(j*a), exp(j*(a-b)) * cos(c/2)], + [exp(j*(a+b)) * cos(c/2), exp(j*a) * sin(c/2)]] + + Args: + matrix(list): 2x2 matrix + Returns: + False if it is not possible otherwise (a, b, c/2) + """ + if abs(matrix[0][0]) < TOLERANCE: + two_a = cmath.phase(matrix[0][1]*matrix[1][0]) % (2*math.pi) + if abs(two_a) < TOLERANCE or abs(two_a) > 2*math.pi-TOLERANCE: + # from 2a==0 (mod 2pi), it follows that a==0 or a==pi, + # w.l.g. we can choose a==0 because (see U above) + # c/2 -> c/2 + pi would have the same effect as as a==0 -> a==pi. + a = 0 + else: + a = two_a/2. + two_b = cmath.phase(matrix[1][0])-cmath.phase(matrix[0][1]) + possible_b = [(two_b/2.) % (2*math.pi), + (two_b/2.+math.pi) % (2*math.pi)] + possible_c_half = [0, math.pi] + found = False + for b, c_half in itertools.product(possible_b, possible_c_half): + if _test_parameters(matrix, a, b, c_half): + found = True + break + assert found # It should work for all matrices with matrix[0][0]==0. + return (a, b, c_half) + + elif abs(matrix[0][1]) < TOLERANCE: + two_a = cmath.phase(-matrix[0][0] * matrix[1][1]) % (2*math.pi) + if abs(two_a) < TOLERANCE or abs(two_a) > 2*math.pi-TOLERANCE: + # from 2a==0 (mod 2pi), it follows that a==0 or a==pi, + # w.l.g. we can choose a==0 because (see U above) + # c/2 -> c/2 + pi would have the same effect as as a==0 -> a==pi. + a = 0 + else: + a = two_a/2. + b = 0 + possible_c_half = [math.pi/2., 3./2.*math.pi] + found = False + for c_half in possible_c_half: + if _test_parameters(matrix, a, b, c_half): + found = True + return (a, b, c_half) + return False + + else: + two_a = cmath.phase(-1.*matrix[0][0]*matrix[1][1]) % (2*math.pi) + if abs(two_a) < TOLERANCE or abs(two_a) > 2*math.pi-TOLERANCE: + # from 2a==0 (mod 2pi), it follows that a==0 or a==pi, + # w.l.g. we can choose a==0 because (see U above) + # c/2 -> c/2 + pi would have the same effect as as a==0 -> a==pi. + a = 0 + else: + a = two_a/2. + two_b = cmath.phase(matrix[1][0])-cmath.phase(matrix[0][1]) + possible_b = [(two_b/2.) % (2*math.pi), + (two_b/2.+math.pi) % (2*math.pi)] + tmp = math.acos(abs(matrix[1][0])) + possible_c_half = [tmp % (2*math.pi), + (tmp+math.pi) % (2*math.pi), + (-1.*tmp) % (2*math.pi), + (-1.*tmp+math.pi) % (2*math.pi)] + found = False + for b, c_half in itertools.product(possible_b, possible_c_half): + if _test_parameters(matrix, a, b, c_half): + found = True + return (a, b, c_half) + return False + + +def _decompose_carb1qubit(cmd): + """ + Decompose the single controlled 1 qubit gate into CNOT, Rz, Ry, C(Ph). + + See Nielsen and Chuang chapter 4.3. + + An arbitrary one qubit gate matrix can be writen as + U = [[exp(j*(a-b/2-d/2))*cos(c/2), -exp(j*(a-b/2+d/2))*sin(c/2)], + [exp(j*(a+b/2-d/2))*sin(c/2), exp(j*(a+b/2+d/2))*cos(c/2)]] + where a,b,c,d are real numbers. + Then U = exp(j*a) Rz(b) Ry(c) Rz(d). + + Then C(U) = C(exp(ja)) * A * CNOT * B * CNOT * C with + A = Rz(b) * Ry(c/2) + B = Ry(-c/2) * Rz(-(d+b)/2) + C = Rz((d-b)/2) + Note that a == 0 if U is element of SU(2). Also note that + the controlled phase C(exp(ia)) can be implemented with single + qubit gates. + + If the one qubit gate matrix can be writen as + V = [[-sin(c/2) * exp(j*a), exp(j*(a-b)) * cos(c/2)], + [exp(j*(a+b)) * cos(c/2), exp(j*a) * sin(c/2)]] + Then C(V) = C(exp(ja))* E * CNOT * D with + E = Rz(b)Ry(c/2) + D = Ry(-c/2)Rz(-b) + This improvement is important for C(Y) or C(Z) + + For a proof follow Lemma 5.5 of Barenco et al. + """ + matrix = cmd.gate.matrix.tolist() + qb = cmd.qubits + eng = cmd.engine + + # Case 1: Unitary matrix which can be written in the form of V: + parameters_for_v = _recognize_v(matrix) + if parameters_for_v: + a, b, c_half = parameters_for_v + if Rz(-b) != Rz(0): + Rz(-b) | qb + if Ry(-c_half) != Ry(0): + Ry(-c_half) | qb + with Control(eng, cmd.control_qubits): + X | qb + if Ry(c_half) != Ry(0): + Ry(c_half) | qb + if Rz(b) != Rz(0): + Rz(b) | qb + if a != 0: + with Control(eng, cmd.control_qubits): + Ph(a) | qb + + # Case 2: General matrix U: + else: + a, b_half, c_half, d_half = arb1q._find_parameters(matrix) + d = 2*d_half + b = 2*b_half + if Rz((d-b)/2.) != Rz(0): + Rz((d-b)/2.) | qb + with Control(eng, cmd.control_qubits): + X | qb + if Rz(-(d+b)/2.) != Rz(0): + Rz(-(d+b)/2.) | qb + if Ry(-c_half) != Ry(0): + Ry(-c_half) | qb + with Control(eng, cmd.control_qubits): + X | qb + if Ry(c_half) != Ry(0): + Ry(c_half) | qb + if Rz(b) != Rz(0): + Rz(b) | qb + if a != 0: + with Control(eng, cmd.control_qubits): + Ph(a) | qb + + +all_defined_decomposition_rules = [ + DecompositionRule(BasicGate, _decompose_carb1qubit, _recognize_carb1qubit) +] diff --git a/projectq/setups/decompositions/carb1qubit2cnotrzandry_test.py b/projectq/setups/decompositions/carb1qubit2cnotrzandry_test.py new file mode 100644 index 000000000..3b7f433b3 --- /dev/null +++ b/projectq/setups/decompositions/carb1qubit2cnotrzandry_test.py @@ -0,0 +1,146 @@ +# 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 projectq.setups.decompositions.carb1qubit2cnotrzandry.py." + +import numpy as np +import pytest + +from projectq.backends import Simulator +from projectq.cengines import (AutoReplacer, DecompositionRuleSet, + DummyEngine, InstructionFilter, MainEngine) +from projectq.meta import Control +from projectq.ops import (BasicGate, ClassicalInstructionGate, Measure, + Ph, R, Rx, Ry, Rz, X, XGate) +from projectq.setups.decompositions import arb1qubit2rzandry_test as arb1q_t + +from . import carb1qubit2cnotrzandry as carb1q + + +def test_recognize_correct_gates(): + saving_backend = DummyEngine(save_commands=True) + eng = MainEngine(backend=saving_backend) + qubit = eng.allocate_qubit() + ctrl_qubit = eng.allocate_qubit() + eng.flush() + with Control(eng, ctrl_qubit): + Ph(0.1) | qubit + R(0.2) | qubit + Rx(0.3) | qubit + X | qubit + eng.flush(deallocate_qubits=True) + # Don't test initial two allocate and flush and trailing deallocate + # and flush gate. + for cmd in saving_backend.received_commands[3:-3]: + assert carb1q._recognize_carb1qubit(cmd) + + +def test_recognize_incorrect_gates(): + saving_backend = DummyEngine(save_commands=True) + eng = MainEngine(backend=saving_backend) + qubit = eng.allocate_qubit() + ctrl_qubit = eng.allocate_qubit() + ctrl_qureg = eng.allocate_qureg(2) + eng.flush() + with Control(eng, ctrl_qubit): + # Does not have matrix attribute: + BasicGate() | qubit + # Two qubit gate: + two_qubit_gate = BasicGate() + two_qubit_gate.matrix = [[1, 0, 0, 0], [0, 1, 0, 0], + [0, 0, 1, 0], [0, 0, 0, 1]] + two_qubit_gate | qubit + with Control(eng, ctrl_qureg): + # Too many Control qubits: + Rx(0.3) | qubit + eng.flush(deallocate_qubits=True) + for cmd in saving_backend.received_commands: + assert not carb1q._recognize_carb1qubit(cmd) + + +def _decomp_gates(eng, cmd): + g = cmd.gate + if isinstance(g, ClassicalInstructionGate): + return True + if len(cmd.control_qubits) == 0: + if (isinstance(cmd.gate, Ry) or + isinstance(cmd.gate, Rz) or + isinstance(cmd.gate, Ph)): + return True + if len(cmd.control_qubits) == 1: + if (isinstance(cmd.gate, XGate) or + isinstance(cmd.gate, Ph)): + return True + return False + + +@pytest.mark.parametrize("gate_matrix", [[[1, 0], [0, -1]], + [[0, -1j], [1j, 0]]]) +def test_recognize_v(gate_matrix): + assert carb1q._recognize_v(gate_matrix) + + +@pytest.mark.parametrize("gate_matrix", arb1q_t.create_test_matrices()) +def test_decomposition(gate_matrix): + # Create single qubit gate with gate_matrix + test_gate = BasicGate() + test_gate.matrix = np.matrix(gate_matrix) + + for basis_state in ([1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], + [0, 0, 0, 1]): + correct_dummy_eng = DummyEngine(save_commands=True) + correct_eng = MainEngine(backend=Simulator(), + engine_list=[correct_dummy_eng]) + + rule_set = DecompositionRuleSet(modules=[carb1q]) + test_dummy_eng = DummyEngine(save_commands=True) + test_eng = MainEngine(backend=Simulator(), + engine_list=[AutoReplacer(rule_set), + InstructionFilter(_decomp_gates), + test_dummy_eng]) + test_sim = test_eng.backend + correct_sim = correct_eng.backend + + correct_qb = correct_eng.allocate_qubit() + correct_ctrl_qb = correct_eng.allocate_qubit() + correct_eng.flush() + test_qb = test_eng.allocate_qubit() + test_ctrl_qb = test_eng.allocate_qubit() + test_eng.flush() + + correct_sim.set_wavefunction(basis_state, correct_qb + + correct_ctrl_qb) + test_sim.set_wavefunction(basis_state, test_qb + test_ctrl_qb) + + with Control(test_eng, test_ctrl_qb): + test_gate | test_qb + with Control(correct_eng, correct_ctrl_qb): + test_gate | correct_qb + + test_eng.flush() + correct_eng.flush() + + assert correct_dummy_eng.received_commands[3].gate == test_gate + assert test_dummy_eng.received_commands[3].gate != test_gate + + for fstate in ['00', '01', '10', '11']: + test = test_sim.get_amplitude(fstate, test_qb + test_ctrl_qb) + correct = correct_sim.get_amplitude(fstate, correct_qb + + correct_ctrl_qb) + assert correct == pytest.approx(test, rel=1e-12, abs=1e-12) + + Measure | test_qb + test_ctrl_qb + Measure | correct_qb + correct_ctrl_qb + test_eng.flush(deallocate_qubits=True) + correct_eng.flush(deallocate_qubits=True) diff --git a/projectq/setups/decompositions/cnu2toffoliandcu.py b/projectq/setups/decompositions/cnu2toffoliandcu.py new file mode 100644 index 000000000..8e06e02eb --- /dev/null +++ b/projectq/setups/decompositions/cnu2toffoliandcu.py @@ -0,0 +1,68 @@ +# 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. + +""" +Registers a decomposition rule for multi-controlled gates. + +Implements the decomposition of Nielsen and Chuang (Fig. 4.10) which +decomposes a C^n(U) gate into a sequence of 2 * (n-1) Toffoli gates and one +C(U) gate by using (n-1) ancilla qubits and circuit depth of 2n-1. +""" + +from projectq.cengines import DecompositionRule +from projectq.meta import get_control_count, Compute, Control, Uncompute +from projectq.ops import BasicGate, Toffoli, XGate + + +def _recognize_CnU(cmd): + """ + Recognize an arbitrary gate which has n>=2 control qubits, except a + Toffoli gate. + """ + if get_control_count(cmd) == 2: + if not isinstance(cmd.gate, XGate): + return True + elif get_control_count(cmd) > 2: + return True + return False + + +def _decompose_CnU(cmd): + """ + Decompose a multi-controlled gate U into a single-controlled U. + + It uses (n-1) work qubits and 2 * (n-1) Toffoli gates. + """ + eng = cmd.engine + qubits = cmd.qubits + ctrl_qureg = cmd.control_qubits + gate = cmd.gate + n = get_control_count(cmd) + ancilla_qureg = eng.allocate_qureg(n-1) + + with Compute(eng): + Toffoli | (ctrl_qureg[0], ctrl_qureg[1], ancilla_qureg[0]) + for ctrl_index in range(2,n): + Toffoli | (ctrl_qureg[ctrl_index], ancilla_qureg[ctrl_index-2], + ancilla_qureg[ctrl_index-1]) + + with Control(eng, ancilla_qureg[-1]): + gate | qubits + + Uncompute(eng) + + +all_defined_decomposition_rules = [ + DecompositionRule(BasicGate, _decompose_CnU, _recognize_CnU) +] diff --git a/projectq/setups/decompositions/cnu2toffoliandcu_test.py b/projectq/setups/decompositions/cnu2toffoliandcu_test.py new file mode 100644 index 000000000..1987a8e2b --- /dev/null +++ b/projectq/setups/decompositions/cnu2toffoliandcu_test.py @@ -0,0 +1,131 @@ +# 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 projectq.setups.decompositions.cnu2toffoliandcu." + +import pytest + +from projectq.backends import Simulator +from projectq.cengines import (AutoReplacer, DecompositionRuleSet, + DummyEngine, InstructionFilter, MainEngine) +from projectq.meta import Control +from projectq.ops import (ClassicalInstructionGate, Measure, Ph, QFT, Rx, Ry, + X, XGate) + +from . import cnu2toffoliandcu + + +def test_recognize_correct_gates(): + saving_backend = DummyEngine(save_commands=True) + eng = MainEngine(backend=saving_backend) + qubit = eng.allocate_qubit() + ctrl_qureg = eng.allocate_qureg(2) + ctrl_qureg2 = eng.allocate_qureg(3) + eng.flush() + with Control(eng, ctrl_qureg): + Ph(0.1) | qubit + Ry(0.2) | qubit + with Control(eng, ctrl_qureg2): + QFT | qubit + ctrl_qureg + X | qubit + eng.flush() # To make sure gates arrive before deallocate gates + eng.flush(deallocate_qubits=True) + # Don't test initial 6 allocate and flush and trailing deallocate + # and two flush gates. + for cmd in saving_backend.received_commands[7:-8]: + assert cnu2toffoliandcu._recognize_CnU(cmd) + + +def test_recognize_incorrect_gates(): + saving_backend = DummyEngine(save_commands=True) + eng = MainEngine(backend=saving_backend) + qubit = eng.allocate_qubit() + ctrl_qubit = eng.allocate_qubit() + ctrl_qureg = eng.allocate_qureg(2) + eng.flush() + with Control(eng, ctrl_qubit): + Rx(0.3) | qubit + X | qubit + with Control(eng, ctrl_qureg): + X | qubit + eng.flush(deallocate_qubits=True) + for cmd in saving_backend.received_commands: + assert not cnu2toffoliandcu._recognize_CnU(cmd) + + +def _decomp_gates(eng, cmd): + g = cmd.gate + if isinstance(g, ClassicalInstructionGate): + return True + if len(cmd.control_qubits) <= 1: + return True + if len(cmd.control_qubits) == 2 and isinstance(cmd.gate, XGate): + return True + return False + + +def test_decomposition(): + for basis_state_index in range(0,16): + basis_state = [0] * 16 + basis_state[basis_state_index] = 1. + correct_dummy_eng = DummyEngine(save_commands=True) + correct_eng = MainEngine(backend=Simulator(), + engine_list=[correct_dummy_eng]) + rule_set = DecompositionRuleSet(modules=[cnu2toffoliandcu]) + test_dummy_eng = DummyEngine(save_commands=True) + test_eng = MainEngine(backend=Simulator(), + engine_list=[AutoReplacer(rule_set), + InstructionFilter(_decomp_gates), + test_dummy_eng]) + test_sim = test_eng.backend + correct_sim = correct_eng.backend + correct_qb = correct_eng.allocate_qubit() + correct_ctrl_qureg = correct_eng.allocate_qureg(3) + correct_eng.flush() + test_qb = test_eng.allocate_qubit() + test_ctrl_qureg = test_eng.allocate_qureg(3) + test_eng.flush() + + correct_sim.set_wavefunction(basis_state, correct_qb + + correct_ctrl_qureg) + test_sim.set_wavefunction(basis_state, test_qb + test_ctrl_qureg) + + with Control(test_eng, test_ctrl_qureg[:2]): + Rx(0.4) | test_qb + with Control(test_eng, test_ctrl_qureg): + Ry(0.6) | test_qb + + with Control(correct_eng, correct_ctrl_qureg[:2]): + Rx(0.4) | correct_qb + with Control(correct_eng, correct_ctrl_qureg): + Ry(0.6) | correct_qb + + test_eng.flush() + correct_eng.flush() + + assert len(correct_dummy_eng.received_commands) == 8 + assert len(test_dummy_eng.received_commands) == 20 + + for fstate in range(16): + binary_state = format(fstate, '04b') + test = test_sim.get_amplitude(binary_state, + test_qb + test_ctrl_qureg) + correct = correct_sim.get_amplitude(binary_state, correct_qb + + correct_ctrl_qureg) + assert correct == pytest.approx(test, rel=1e-12, abs=1e-12) + + Measure | test_qb + test_ctrl_qureg + Measure | correct_qb + correct_ctrl_qureg + test_eng.flush(deallocate_qubits=True) + correct_eng.flush(deallocate_qubits=True) diff --git a/projectq/setups/decompositions/crz2cxandrz.py b/projectq/setups/decompositions/crz2cxandrz.py index a34e63a21..7f90047db 100755 --- a/projectq/setups/decompositions/crz2cxandrz.py +++ b/projectq/setups/decompositions/crz2cxandrz.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/setups/decompositions/entangle.py b/projectq/setups/decompositions/entangle.py index edde59b8b..790f2ec44 100755 --- a/projectq/setups/decompositions/entangle.py +++ b/projectq/setups/decompositions/entangle.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/setups/decompositions/globalphase.py b/projectq/setups/decompositions/globalphase.py index 728828e10..43a4a8d49 100755 --- a/projectq/setups/decompositions/globalphase.py +++ b/projectq/setups/decompositions/globalphase.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/setups/decompositions/ph2r.py b/projectq/setups/decompositions/ph2r.py index 512143415..afc0071b6 100755 --- a/projectq/setups/decompositions/ph2r.py +++ b/projectq/setups/decompositions/ph2r.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/setups/decompositions/qft2crandhadamard.py b/projectq/setups/decompositions/qft2crandhadamard.py index 9765145a1..469bb082a 100755 --- a/projectq/setups/decompositions/qft2crandhadamard.py +++ b/projectq/setups/decompositions/qft2crandhadamard.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/setups/decompositions/r2rzandph.py b/projectq/setups/decompositions/r2rzandph.py index 3b808b2c9..b4c84d1b7 100755 --- a/projectq/setups/decompositions/r2rzandph.py +++ b/projectq/setups/decompositions/r2rzandph.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/setups/decompositions/swap2cnot.py b/projectq/setups/decompositions/swap2cnot.py index 3c85b6db7..e83b2a94d 100755 --- a/projectq/setups/decompositions/swap2cnot.py +++ b/projectq/setups/decompositions/swap2cnot.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/setups/decompositions/time_evolution.py b/projectq/setups/decompositions/time_evolution.py index a0897af94..4a34a4f30 100644 --- a/projectq/setups/decompositions/time_evolution.py +++ b/projectq/setups/decompositions/time_evolution.py @@ -1,3 +1,5 @@ +# 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 @@ -90,7 +92,7 @@ def _decompose_time_evolution_individual_terms(cmd): check_indices = set() # Check that hamiltonian is not identity term, - # Previous or operator should have apply a global phase instead: + # Previous __or__ operator should have apply a global phase instead: assert not term == () # hamiltonian has only a single local operator diff --git a/projectq/setups/decompositions/time_evolution_test.py b/projectq/setups/decompositions/time_evolution_test.py index 224cd0314..4a5e30f54 100644 --- a/projectq/setups/decompositions/time_evolution_test.py +++ b/projectq/setups/decompositions/time_evolution_test.py @@ -1,3 +1,5 @@ +# 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 @@ -178,14 +180,14 @@ def build_matrix(list_single_matrices): res = list_single_matrices[0] for i in range(1, len(list_single_matrices)): res = sps.kron(res, list_single_matrices[i]) - return res + return res.tocsc() - id_sp = sps.identity(2, format="csr", dtype=complex) - x_sp = sps.csr_matrix([[0., 1.], [1., 0.]], dtype=complex) - y_sp = sps.csr_matrix([[0., -1.j], [1.j, 0.]], dtype=complex) - z_sp = sps.csr_matrix([[1., 0.], [0., -1.]], dtype=complex) + id_sp = sps.identity(2, format="csc", dtype=complex) + x_sp = sps.csc_matrix([[0., 1.], [1., 0.]], dtype=complex) + y_sp = sps.csc_matrix([[0., -1.j], [1.j, 0.]], dtype=complex) + z_sp = sps.csc_matrix([[1., 0.], [0., -1.]], dtype=complex) - matrix1 = (sps.identity(2**5, format="csr", dtype=complex) * 0.6 * + matrix1 = (sps.identity(2**5, format="csc", dtype=complex) * 0.6 * 1.1 * -1.0j) step1 = scipy.sparse.linalg.expm(matrix1).dot(init_wavefunction) assert numpy.allclose(step1, final_wavefunction1) diff --git a/projectq/setups/decompositions/toffoli2cnotandtgate.py b/projectq/setups/decompositions/toffoli2cnotandtgate.py index b24b2540c..90c7d18a7 100755 --- a/projectq/setups/decompositions/toffoli2cnotandtgate.py +++ b/projectq/setups/decompositions/toffoli2cnotandtgate.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/setups/default.py b/projectq/setups/default.py index eb8f98531..efb425f9b 100755 --- a/projectq/setups/default.py +++ b/projectq/setups/default.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/setups/ibm.py b/projectq/setups/ibm.py index 65599e57f..ddfe144a4 100755 --- a/projectq/setups/ibm.py +++ b/projectq/setups/ibm.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/setups/ibm_test.py b/projectq/setups/ibm_test.py index 9ea09a530..7087be569 100644 --- a/projectq/setups/ibm_test.py +++ b/projectq/setups/ibm_test.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/tests/__init__.py b/projectq/tests/__init__.py index 53f003f0a..ee1451dcd 100755 --- a/projectq/tests/__init__.py +++ b/projectq/tests/__init__.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/tests/_factoring_test.py b/projectq/tests/_factoring_test.py index 4a240d4af..2116e00c9 100755 --- a/projectq/tests/_factoring_test.py +++ b/projectq/tests/_factoring_test.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/types/__init__.py b/projectq/types/__init__.py index 089502474..1abaea68b 100755 --- a/projectq/types/__init__.py +++ b/projectq/types/__init__.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/types/_qubit.py b/projectq/types/_qubit.py index d9601e2f1..1d3d2a137 100755 --- a/projectq/types/_qubit.py +++ b/projectq/types/_qubit.py @@ -1,3 +1,5 @@ +# 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 diff --git a/projectq/types/_qubit_test.py b/projectq/types/_qubit_test.py index c493ba31c..f6702c576 100755 --- a/projectq/types/_qubit_test.py +++ b/projectq/types/_qubit_test.py @@ -1,3 +1,5 @@ +# 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 diff --git a/requirements.txt b/requirements.txt index d767a8192..a2dda1658 100755 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ numpy future -pytest>=3.0 +pytest>=3.1 pybind11>=1.7,<2.2.0 requests scipy diff --git a/setup.py b/setup.py index 4d78e451a..604f006af 100755 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ long_description = open('README.rst').read() # Read in requirements.txt -with open ('requirements.txt', 'r') as f_requirements: +with open('requirements.txt', 'r') as f_requirements: requirements = f_requirements.readlines() requirements = [r.strip() for r in requirements]