Skip to content

Commit

Permalink
Add state preparation (#254)
Browse files Browse the repository at this point in the history
  • Loading branch information
damiansteiger committed Aug 9, 2018
1 parent d3631e2 commit 0d1a78f
Show file tree
Hide file tree
Showing 6 changed files with 237 additions and 0 deletions.
1 change: 1 addition & 0 deletions projectq/ops/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,4 @@
from ._time_evolution import TimeEvolution
from ._uniformly_controlled_rotation import (UniformlyControlledRy,
UniformlyControlledRz)
from ._state_prep import StatePreparation
57 changes: 57 additions & 0 deletions projectq/ops/_state_prep.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Copyright 2018 ProjectQ-Framework (www.projectq.ch)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from ._basics import BasicGate


class StatePreparation(BasicGate):
"""
Gate for transforming qubits in state |0> to any desired quantum state.
"""
def __init__(self, final_state):
"""
Initialize StatePreparation gate.
Example:
.. code-block:: python
qureg = eng.allocate_qureg(2)
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.
Args:
final_state(list[complex]): wavefunction of the desired
quantum state. len(final_state) must
be 2**len(qureg). Must be normalized!
"""
BasicGate.__init__(self)
self.final_state = list(final_state)

def __str__(self):
return "StatePreparation"

def __eq__(self, other):
if isinstance(other, self.__class__):
return self.final_state == other.final_state
return False

def __ne__(self, other):
return not self.__eq__(other)

def __hash__(self):
return hash("StatePreparation(" + str(self.final_state) + ")")
34 changes: 34 additions & 0 deletions projectq/ops/_state_prep_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Copyright 2018 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.ops._state_prep."""

import projectq

from projectq.ops import _state_prep, X


def test_equality_and_hash():
gate1 = _state_prep.StatePreparation([0.5, -0.5, 0.5, -0.5])
gate2 = _state_prep.StatePreparation([0.5, -0.5, 0.5, -0.5])
gate3 = _state_prep.StatePreparation([0.5, -0.5, 0.5, 0.5])
assert gate1 == gate2
assert hash(gate1) == hash(gate2)
assert gate1 != gate3
assert gate1 != X


def test_str():
gate1 = _state_prep.StatePreparation([0, 1])
assert str(gate1) == "StatePreparation"
2 changes: 2 additions & 0 deletions projectq/setups/decompositions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
r2rzandph,
rx2rz,
ry2rz,
stateprep2cnot,
swap2cnot,
toffoli2cnotandtgate,
time_evolution,
Expand All @@ -45,6 +46,7 @@
r2rzandph,
rx2rz,
ry2rz,
stateprep2cnot,
swap2cnot,
toffoli2cnotandtgate,
time_evolution,
Expand Down
79 changes: 79 additions & 0 deletions projectq/setups/decompositions/stateprep2cnot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Copyright 2018 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 decomposition for StatePreparation.
"""

import cmath
import math

from projectq.cengines import DecompositionRule
from projectq.meta import Control, Dagger
from projectq.ops import (StatePreparation, Ry, Rz, UniformlyControlledRy,
UniformlyControlledRz, Ph)


def _decompose_state_preparation(cmd):
"""
Implements state preparation based on arXiv:quant-ph/0407010v1.
"""
eng = cmd.engine
assert len(cmd.qubits) == 1
num_qubits = len(cmd.qubits[0])
qureg = cmd.qubits[0]
final_state = cmd.gate.final_state
if len(final_state) != 2**num_qubits:
raise ValueError("Length of final_state is invalid.")
with Control(eng, cmd.control_qubits):
# As in the paper reference, we implement the inverse:
with Dagger(eng):
# Cancel all the relative phases
phase_of_blocks = []
for amplitude in final_state:
phase_of_blocks.append(cmath.phase(amplitude))
for target_qubit in range(len(qureg)):
angles = []
phase_of_next_blocks = []
for block in range(2**(len(qureg)-target_qubit-1)):
phase0 = phase_of_blocks[2*block]
phase1 = phase_of_blocks[2*block+1]
angles.append(phase0 - phase1)
phase_of_next_blocks.append((phase0 + phase1)/2.)
UniformlyControlledRz(angles) | (qureg[(target_qubit+1):],
qureg[target_qubit])
phase_of_blocks = phase_of_next_blocks
# Cancel global phase
Ph(-phase_of_blocks[0]) | qureg[-1]
# Remove amplitudes from states which contain a bit value 1:
abs_of_blocks = []
for amplitude in final_state:
abs_of_blocks.append(abs(amplitude))
for target_qubit in range(len(qureg)):
angles = []
abs_of_next_blocks = []
for block in range(2**(len(qureg)-target_qubit-1)):
a0 = abs_of_blocks[2*block]
a1 = abs_of_blocks[2*block+1]
angles.append(-2. * math.acos(a0/math.sqrt(a0**2 + a1**2)))
abs_of_next_blocks.append(math.sqrt(a0**2 + a1**2))
UniformlyControlledRy(angles) | (qureg[(target_qubit+1):],
qureg[target_qubit])
abs_of_blocks = abs_of_next_blocks


#: Decomposition rules
all_defined_decomposition_rules = [
DecompositionRule(StatePreparation, _decompose_state_preparation)
]
64 changes: 64 additions & 0 deletions projectq/setups/decompositions/stateprep2cnot_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Copyright 2018 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.stateprep2cnot."""

import cmath
from copy import deepcopy
import math

import numpy as np
import pytest

import projectq
from projectq.ops import All, Command, Measure, Ry, Rz, StatePreparation, Ph
from projectq.setups import restrictedgateset
from projectq.types import WeakQubitRef

import projectq.setups.decompositions.stateprep2cnot as stateprep2cnot


def test_wrong_final_state():
qb0 = WeakQubitRef(engine=None, idx=0)
qb1 = WeakQubitRef(engine=None, idx=1)
cmd = Command(None, StatePreparation([0, 1j]), qubits=([qb0, qb1],))
with pytest.raises(ValueError):
stateprep2cnot._decompose_state_preparation(cmd)


@pytest.mark.parametrize("n_qubits", [1, 2, 3, 4])
def test_state_preparation(n_qubits):
engine_list = restrictedgateset.get_engine_list(
one_qubit_gates=(Ry, Rz, Ph))
eng = projectq.MainEngine(engine_list=engine_list)
qureg = eng.allocate_qureg(n_qubits)
eng.flush()

f_state = [0.2+0.1*x*cmath.exp(0.1j+0.2j*x) for x in range(2**n_qubits)]
norm = 0
for amplitude in f_state:
norm += abs(amplitude)**2
f_state = [x / math.sqrt(norm) for x in f_state]

StatePreparation(f_state) | qureg
eng.flush()

wavefunction = deepcopy(eng.backend.cheat()[1])
# Test that simulator hasn't reordered wavefunction
mapping = eng.backend.cheat()[0]
for key in mapping:
assert mapping[key] == key
All(Measure) | qureg
eng.flush()
assert np.allclose(wavefunction, f_state, rtol=1e-10, atol=1e-10)

0 comments on commit 0d1a78f

Please sign in to comment.