Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a convenient state preparation for computational basis states #289

Merged
merged 26 commits into from
Dec 22, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
bb2cb91
added a BasisState state preparation and corresponding unit tests
cgogolin Nov 27, 2018
3423084
added BasisState to the docs
cgogolin Nov 27, 2018
e404970
change in the documentation of StatePreparation
cgogolin Nov 27, 2018
05a2f21
renamed BasisState to FlipBits as suggested by @thomashaener
cgogolin Dec 11, 2018
77db87b
removed note in StatePreparation
cgogolin Dec 11, 2018
9f1b0f6
FlipBits is now accepting also array(bool) and strings such as '0101'
cgogolin Dec 11, 2018
7e7be9f
renamed BasisState to FlipBits also in docs
cgogolin Dec 11, 2018
fe1e3f4
reformating in docstring to minimize diff
cgogolin Dec 11, 2018
6aab84e
moved FlipBits to _gates.py
cgogolin Dec 14, 2018
582092a
include bits in __str__ and use that in __hash__
cgogolin Dec 14, 2018
26161d7
allow to flip bits from int input
cgogolin Dec 14, 2018
5ff75b6
allow lists/strings and integers with binary representation shorter t…
cgogolin Dec 14, 2018
3ae9011
simplified __eq__
cgogolin Dec 14, 2018
80315fc
flip complement of bits for negative integer inputs
cgogolin Dec 17, 2018
6be145a
store bits to flip always as an int
cgogolin Dec 18, 2018
0727936
correction to 6be145af8134042afbc7af039ff9cd3d3621dc38
cgogolin Dec 18, 2018
6f32a1f
no longer generate the list of bits to flip in advance
cgogolin Dec 18, 2018
d712de1
simplified for loop
cgogolin Dec 18, 2018
be5e716
added test on concatenated arrays of qubits
cgogolin Dec 18, 2018
30103c7
changed the behavior for negative numbers, adjusted the documentation…
cgogolin Dec 20, 2018
60cec73
simplified conditional for raising ValueError
cgogolin Dec 21, 2018
7bf6ae3
pylinted
cgogolin Dec 21, 2018
16dc88e
pep8ed
cgogolin Dec 21, 2018
5a8a972
moved test to _gates_test.py
cgogolin Dec 21, 2018
7e67f52
more pep8ing
cgogolin Dec 21, 2018
92184cc
Remove unused import.
thomashaener Dec 22, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/projectq.ops.rst
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ The operations collection consists of various default gates and is a work-in-pro
projectq.ops.UniformlyControlledRy
projectq.ops.UniformlyControlledRz
projectq.ops.StatePreparation
projectq.ops.FlipBits


Module contents
Expand Down
54 changes: 53 additions & 1 deletion projectq/ops/_gates.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@
FastForwardingGate,
BasicMathGate)
from ._command import apply_command
from projectq.types import BasicQubit


class HGate(SelfInverseGate):
Expand Down Expand Up @@ -338,3 +337,56 @@ def get_inverse(self):

#: Shortcut (instance of) :class:`projectq.ops.BarrierGate`
Barrier = BarrierGate()


class FlipBits(SelfInverseGate):
""" Gate for flipping qubits by means of XGates """
def __init__(self, bits_to_flip):
"""
Initialize FlipBits gate.

Example:
.. code-block:: python

qureg = eng.allocate_qureg(2)
FlipBits([0, 1]) | qureg

Args:
bits_to_flip(list[int]|list[bool]|str|int): int or array of 0/1,
True/False, or string of 0/1 identifying the qubits to flip.
In case of int, the bits to flip are determined from the
binary digits, with the least significant bit corresponding
to qureg[0]. If bits_to_flip is negative, exactly all qubits
which would not be flipped for the input -bits_to_flip-1 are
flipped, i.e., bits_to_flip=-1 flips all qubits.
"""
SelfInverseGate.__init__(self)
if isinstance(bits_to_flip, int):
self.bits_to_flip = bits_to_flip
else:
self.bits_to_flip = 0
for i in reversed(list(bits_to_flip)):
bit = 0b1 if i == '1' or i == 1 or i is True else 0b0
self.bits_to_flip = (self.bits_to_flip << 1) | bit

def __str__(self):
return "FlipBits("+str(self.bits_to_flip)+")"

def __or__(self, qubits):
quregs_tuple = self.make_tuple_of_qureg(qubits)
if len(quregs_tuple) > 1:
raise ValueError(self.__str__()+' can only be applied to qubits,'
'quregs, arrays of qubits, and tuples with one'
'individual qubit')
for qureg in quregs_tuple:
for i, qubit in enumerate(qureg):
if (self.bits_to_flip >> i) & 1:
XGate() | qubit

def __eq__(self, other):
if isinstance(other, self.__class__):
return self.bits_to_flip == other.bits_to_flip
cgogolin marked this conversation as resolved.
Show resolved Hide resolved
return False

def __hash__(self):
return hash(self.__str__())
82 changes: 79 additions & 3 deletions projectq/ops/_gates_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@
import numpy as np
import pytest

from projectq.ops import (get_inverse, SelfInverseGate, BasicRotationGate,
ClassicalInstructionGate, FastForwardingGate,
BasicGate)
from projectq import MainEngine
from projectq.ops import (All, FlipBits, get_inverse, SelfInverseGate,
BasicRotationGate, ClassicalInstructionGate,
FastForwardingGate, BasicGate, Measure)

from projectq.ops import _gates

Expand Down Expand Up @@ -217,3 +218,78 @@ def test_barrier_gate():
assert str(gate) == "Barrier"
assert gate.get_inverse() == _gates.BarrierGate()
assert isinstance(_gates.Barrier, _gates.BarrierGate)


def test_flip_bits_equality_and_hash():
gate1 = _gates.FlipBits([1, 0, 0, 1])
gate2 = _gates.FlipBits([1, 0, 0, 1])
gate3 = _gates.FlipBits([0, 1, 0, 1])
assert gate1 == gate2
assert hash(gate1) == hash(gate2)
assert gate1 != gate3
assert gate1 != _gates.X


def test_flip_bits_str():
gate1 = _gates.FlipBits([0, 0, 1])
assert str(gate1) == "FlipBits(4)"


def test_error_on_tuple_input():
with pytest.raises(ValueError):
_gates.FlipBits(2) | (None, None)


flip_bits_testdata = [
([0, 1, 0, 1], '0101'),
([1, 0, 1, 0], '1010'),
([False, True, False, True], '0101'),
('0101', '0101'),
('1111', '1111'),
('0000', '0000'),
(8, '0001'),
(11, '1101'),
(1, '1000'),
(-1, '1111'),
(-2, '0111'),
(-3, '1011'),
]


@pytest.mark.parametrize("bits_to_flip, result", flip_bits_testdata)
def test_simulator_flip_bits(bits_to_flip, result):
eng = MainEngine()
qubits = eng.allocate_qureg(4)
FlipBits(bits_to_flip) | qubits
eng.flush()
assert pytest.approx(eng.backend.get_probability(result, qubits)) == 1.
All(Measure) | qubits


def test_flip_bits_can_be_applied_to_various_qubit_qureg_formats():
eng = MainEngine()
qubits = eng.allocate_qureg(4)
eng.flush()
assert pytest.approx(eng.backend.get_probability('0000', qubits)) == 1.
FlipBits([0, 1, 1, 0]) | qubits
eng.flush()
assert pytest.approx(eng.backend.get_probability('0110', qubits)) == 1.
FlipBits([1]) | qubits[0]
eng.flush()
assert pytest.approx(eng.backend.get_probability('1110', qubits)) == 1.
FlipBits([1]) | (qubits[0], )
eng.flush()
assert pytest.approx(eng.backend.get_probability('0110', qubits)) == 1.
FlipBits([1, 1]) | [qubits[0], qubits[1]]
eng.flush()
assert pytest.approx(eng.backend.get_probability('1010', qubits)) == 1.
FlipBits(-1) | qubits
eng.flush()
assert pytest.approx(eng.backend.get_probability('0101', qubits)) == 1.
FlipBits(-4) | [qubits[0], qubits[1], qubits[2], qubits[3]]
eng.flush()
assert pytest.approx(eng.backend.get_probability('0110', qubits)) == 1.
FlipBits(2) | [qubits[0]] + [qubits[1], qubits[2]]
eng.flush()
assert pytest.approx(eng.backend.get_probability('0010', qubits)) == 1.
All(Measure) | qubits
4 changes: 2 additions & 2 deletions projectq/ops/_qubit_operator.py
Original file line number Diff line number Diff line change
Expand Up @@ -362,9 +362,9 @@ def get_inverse(self):
Raises:
NotInvertible: Not implemented for QubitOperators which have
multiple terms or a coefficient with absolute value
not equal to 1.
not equal to 1.
"""

if len(self.terms) == 1:
(term, coefficient), = self.terms.items()
if (not abs(coefficient) < 1 - EQ_TOLERANCE and not
Expand Down
6 changes: 3 additions & 3 deletions projectq/ops/_state_prep.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ def __init__(self, final_state):
StatePreparation([0.5, -0.5j, -0.5, 0.5]) | qureg

Note:
The amplitude of state k is final_state[k]. When the state k is
written in binary notation, then qureg[0] denotes the qubit
whose state corresponds to the least significant bit of k.
final_state[k] is taken to be the amplitude of the computational
basis state whose string is equal to the binary representation
of k.

Args:
final_state(list[complex]): wavefunction of the desired
Expand Down