forked from quantumlib/Cirq
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Efficient 2q state preparation methods using CZ and SQRT_ISWAP (quant…
…umlib#4707) * Two qubit state preparation using sqrt(iswap) and cz * Improve numerical accuracy and additional improvements * Use circuit.final_state_vector to populate intermediate state matrix instead of hardcoding
- Loading branch information
1 parent
c0fabc5
commit e83bdf1
Showing
4 changed files
with
198 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
102 changes: 102 additions & 0 deletions
102
cirq-core/cirq/optimizers/two_qubit_state_preparation.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
# Copyright 2021 The Cirq Developers | ||
# | ||
# 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 | ||
# | ||
# https://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. | ||
|
||
"""Utility methods for efficiently preparing two qubit states.""" | ||
|
||
from typing import List, TYPE_CHECKING | ||
import numpy as np | ||
|
||
from cirq import ops, qis, circuits | ||
from cirq.optimizers import decompositions | ||
|
||
if TYPE_CHECKING: | ||
import cirq | ||
|
||
|
||
def _1q_matrices_to_ops(g0, g1, q0, q1, include_identity=False): | ||
ret = [] | ||
for g, q in zip(map(decompositions.single_qubit_matrix_to_phxz, [g0, g1]), [q0, q1]): | ||
if g is not None: | ||
ret.append(g.on(q)) | ||
elif include_identity: | ||
ret.append(ops.I.on(q)) | ||
return ret | ||
|
||
|
||
def prepare_two_qubit_state_using_sqrt_iswap( | ||
q0: 'cirq.Qid', | ||
q1: 'cirq.Qid', | ||
state: 'cirq.STATE_VECTOR_LIKE', | ||
*, | ||
use_sqrt_iswap_inv: bool = True, | ||
) -> List['cirq.Operation']: | ||
"""Prepares the given 2q state from |00> using at-most 1 √iSWAP gate + single qubit rotations. | ||
Entangled states are prepared using exactly 1 √iSWAP gate while product states are prepared | ||
using only single qubit rotations (0 √iSWAP gates) | ||
Args: | ||
q0: The first qubit being operated on. | ||
q1: The other qubit being operated on. | ||
state: 4x1 matrix representing two qubit state vector, ordered as 00, 01, 10, 11. | ||
use_sqrt_iswap_inv: If True, uses `cirq.SQRT_ISWAP_INV` instead of `cirq.SQRT_ISWAP`. | ||
Returns: | ||
List of operations (at-most 1 √iSWAP + single qubit rotations) preparing `state` from |00>. | ||
""" | ||
state = qis.to_valid_state_vector(state, num_qubits=2) | ||
state = state / np.linalg.norm(state) | ||
u, s, vh = np.linalg.svd(state.reshape(2, 2)) | ||
if np.isclose(s[0], 1): | ||
# Product state can be prepare with just single qubit unitaries. | ||
return _1q_matrices_to_ops(u, vh.T, q0, q1, True) | ||
alpha = np.arccos(np.sqrt(np.clip(1 - s[0] * 2 * s[1], 0, 1))) | ||
sqrt_iswap_gate = ops.SQRT_ISWAP_INV if use_sqrt_iswap_inv else ops.SQRT_ISWAP | ||
op_list = [ops.ry(2 * alpha).on(q0), sqrt_iswap_gate.on(q0, q1)] | ||
intermediate_state = circuits.Circuit(op_list).final_state_vector() | ||
u_iSWAP, _, vh_iSWAP = np.linalg.svd(intermediate_state.reshape(2, 2)) | ||
return op_list + _1q_matrices_to_ops( | ||
np.dot(u, np.linalg.inv(u_iSWAP)), np.dot(vh.T, np.linalg.inv(vh_iSWAP.T)), q0, q1 | ||
) | ||
|
||
|
||
def prepare_two_qubit_state_using_cz( | ||
q0: 'cirq.Qid', q1: 'cirq.Qid', state: 'cirq.STATE_VECTOR_LIKE' | ||
) -> List['cirq.Operation']: | ||
"""Prepares the given 2q state from |00> using at-most 1 CZ gate + single qubit rotations. | ||
Entangled states are prepared using exactly 1 CZ gate while product states are prepared | ||
using only single qubit rotations (0 CZ gates) | ||
Args: | ||
q0: The first qubit being operated on. | ||
q1: The other qubit being operated on. | ||
state: 4x1 matrix representing two qubit state vector, ordered as 00, 01, 10, 11. | ||
Returns: | ||
List of operations (at-most 1 CZ + single qubit rotations) preparing `state` from |00>. | ||
""" | ||
state = qis.to_valid_state_vector(state, num_qubits=2) | ||
state = state / np.linalg.norm(state) | ||
u, s, vh = np.linalg.svd(state.reshape(2, 2)) | ||
if np.isclose(s[0], 1): | ||
# Product state can be prepare with just single qubit unitaries. | ||
return _1q_matrices_to_ops(u, vh.T, q0, q1, True) | ||
alpha = np.arccos(np.clip(s[0], 0, 1)) | ||
op_list = [ops.ry(2 * alpha).on(q0), ops.H.on(q1), ops.CZ.on(q0, q1)] | ||
intermediate_state = circuits.Circuit(op_list).final_state_vector() | ||
u_CZ, _, vh_CZ = np.linalg.svd(intermediate_state.reshape(2, 2)) | ||
return op_list + _1q_matrices_to_ops( | ||
np.dot(u, np.linalg.inv(u_CZ)), np.dot(vh.T, np.linalg.inv(vh_CZ.T)), q0, q1 | ||
) |
87 changes: 87 additions & 0 deletions
87
cirq-core/cirq/optimizers/two_qubit_state_preparation_test.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
# Copyright 2021 The Cirq Developers | ||
# | ||
# 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 | ||
# | ||
# https://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 efficient two qubit state preparation methods.""" | ||
|
||
import copy | ||
|
||
import pytest | ||
import numpy as np | ||
import cirq | ||
|
||
|
||
def random_state(seed: float): | ||
return cirq.testing.random_superposition(4, random_state=seed) | ||
|
||
|
||
def states_with_phases(st: np.ndarray): | ||
"""Returns several states similar to st with modified global phases.""" | ||
st = np.array(st, dtype="complex64") | ||
yield st | ||
phases = [np.exp(1j * np.pi / 6), -1j, 1j, -1, np.exp(-1j * np.pi / 28)] | ||
random = np.random.RandomState(1) | ||
for _ in range(3): | ||
curr_st = copy.deepcopy(st) | ||
cirq.to_valid_state_vector(curr_st, num_qubits=2) | ||
for i in range(4): | ||
phase = random.choice(phases) | ||
curr_st[i] *= phase | ||
yield curr_st | ||
|
||
|
||
STATES_TO_PREPARE = [ | ||
*states_with_phases(np.array([1, 0, 0, 0])), | ||
*states_with_phases(np.array([0, 1, 0, 0])), | ||
*states_with_phases(np.array([0, 0, 1, 0])), | ||
*states_with_phases(np.array([0, 0, 0, 1])), | ||
*states_with_phases(np.array([1 / np.sqrt(2), 0, 0, 1 / np.sqrt(2)])), | ||
*states_with_phases(np.array([1 / np.sqrt(2), 0, 0, -1 / np.sqrt(2)])), | ||
*states_with_phases(np.array([0, 1 / np.sqrt(2), 1 / np.sqrt(2), 0])), | ||
*states_with_phases(np.array([0, 1 / np.sqrt(2), -1 / np.sqrt(2), 0])), | ||
*states_with_phases(random_state(97154)), | ||
*states_with_phases(random_state(45375)), | ||
*states_with_phases(random_state(78061)), | ||
*states_with_phases(random_state(61474)), | ||
*states_with_phases(random_state(22897)), | ||
] | ||
|
||
|
||
@pytest.mark.parametrize("state", STATES_TO_PREPARE) | ||
def test_prepare_two_qubit_state_using_cz(state): | ||
state = cirq.to_valid_state_vector(state, num_qubits=2) | ||
q = cirq.LineQubit.range(2) | ||
circuit = cirq.Circuit(cirq.prepare_two_qubit_state_using_cz(*q, state)) | ||
ops_cz = [*circuit.findall_operations(lambda op: op.gate == cirq.CZ)] | ||
ops_2q = [*circuit.findall_operations(lambda op: cirq.num_qubits(op) > 1)] | ||
assert ops_cz == ops_2q | ||
assert len(ops_cz) <= 1 | ||
assert cirq.allclose_up_to_global_phase(circuit.final_state_vector(), state) | ||
|
||
|
||
@pytest.mark.parametrize("state", STATES_TO_PREPARE) | ||
@pytest.mark.parametrize("use_sqrt_iswap_inv", [True, False]) | ||
def test_prepare_two_qubit_state_using_sqrt_iswap(state, use_sqrt_iswap_inv): | ||
state = cirq.to_valid_state_vector(state, num_qubits=2) | ||
q = cirq.LineQubit.range(2) | ||
circuit = cirq.Circuit( | ||
cirq.prepare_two_qubit_state_using_sqrt_iswap( | ||
*q, state, use_sqrt_iswap_inv=use_sqrt_iswap_inv | ||
) | ||
) | ||
sqrt_iswap_gate = cirq.SQRT_ISWAP_INV if use_sqrt_iswap_inv else cirq.SQRT_ISWAP | ||
ops_iswap = [*circuit.findall_operations(lambda op: op.gate == sqrt_iswap_gate)] | ||
ops_2q = [*circuit.findall_operations(lambda op: cirq.num_qubits(op) > 1)] | ||
assert ops_iswap == ops_2q | ||
assert len(ops_iswap) <= 1 | ||
assert cirq.allclose_up_to_global_phase(circuit.final_state_vector(), state) |