diff --git a/docs/projectq.ops.rst b/docs/projectq.ops.rst index 8f8d4cceb..8934dd68c 100755 --- a/docs/projectq.ops.rst +++ b/docs/projectq.ops.rst @@ -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 diff --git a/projectq/ops/_gates.py b/projectq/ops/_gates.py index 530fcce76..d160dc9e3 100755 --- a/projectq/ops/_gates.py +++ b/projectq/ops/_gates.py @@ -44,7 +44,6 @@ FastForwardingGate, BasicMathGate) from ._command import apply_command -from projectq.types import BasicQubit class HGate(SelfInverseGate): @@ -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__()) diff --git a/projectq/ops/_gates_test.py b/projectq/ops/_gates_test.py index 002ab8d20..efcd63b0a 100755 --- a/projectq/ops/_gates_test.py +++ b/projectq/ops/_gates_test.py @@ -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 @@ -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 diff --git a/projectq/ops/_qubit_operator.py b/projectq/ops/_qubit_operator.py index aa9a29f9a..7fb65d1b2 100644 --- a/projectq/ops/_qubit_operator.py +++ b/projectq/ops/_qubit_operator.py @@ -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 diff --git a/projectq/ops/_state_prep.py b/projectq/ops/_state_prep.py index 455a47a52..4ef51879d 100644 --- a/projectq/ops/_state_prep.py +++ b/projectq/ops/_state_prep.py @@ -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