Skip to content

Commit

Permalink
Add FlipBits gate (#289)
Browse files Browse the repository at this point in the history
  • Loading branch information
cgogolin authored and thomashaener committed Dec 22, 2018
1 parent e5cc988 commit 867e68e
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 9 deletions.
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
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

0 comments on commit 867e68e

Please sign in to comment.