Skip to content

Commit

Permalink
Add Uniform State Preparation (#12112)
Browse files Browse the repository at this point in the history
* Update state_preparation.py

* Update state_preparation.py

* Add files via upload

* Update __init__.py

* Update test_generalized_uniform_superposition_gate.py

* Update test_generalized_uniform_superposition_gate.py

* Update test_gate_definitions.py

* made the format consistent with StatePreparation class

* Put description and arguments in init

* replaced assert with raise ValueError

* small mistake in Returns

* added test cases for ValueError cases

* Update state_preparation.py

* incorporate Steve's helpful suggestions

* small bug

* test_case fix

* function for returning M

* oops..forgot "" marks

* Update state_preparation.py

* removed get methods for num_qubit and M

* added power of 2 condition

* included test function when num_qubits is None

* blacked init

* blacked state_prep

* blacked test_generalized_uniform_superposition_gate.py

* reblacked state_prep

* reblacked test_generalized_uniform_superposition_gate.py

* shorterned max line length

* reblacked state_prep

* reblacked state_prep

* for pyline

* pylinted state_preparation.py

* pylinted test_generalized_uniform_superposition_gate.py

* pylinted test_gate_definitions.py

* pylinted test_generalized_uniform_superposition_gate.py

* Added GeneralizedUniformSuperposition gate

Added GeneralizedUniformSuperposition gate class to the StatePreparation file in Circuit library.

* modified:   releasenotes/notes/generalized-uniform-superposition-gate-3bd95ffdf05ef18c.yaml

* Updated release notes based on Steve's suggestions

* Update release note

* implemented the changes

* fixed error

* Update test_uniform_superposition_gate.py

* Update uniform-superposition-gate-3bd95ffdf05ef18c.yaml

* Update uniform-superposition-gate-3bd95ffdf05ef18c.yaml

* Update qiskit/circuit/library/data_preparation/state_preparation.py

Sounds good!

Co-authored-by: Julien Gacon <[email protected]>

* Update qiskit/circuit/library/data_preparation/state_preparation.py

Okay!

Co-authored-by: Julien Gacon <[email protected]>

* Update qiskit/circuit/library/data_preparation/state_preparation.py

oh that's interesting...I didn't know that. Thanks for the info!

Co-authored-by: Julien Gacon <[email protected]>

* Update releasenotes/notes/uniform-superposition-gate-3bd95ffdf05ef18c.yaml

Ahhh...that's nice!

Co-authored-by: Julien Gacon <[email protected]>

* Update releasenotes/notes/uniform-superposition-gate-3bd95ffdf05ef18c.yaml

You're quite right, will help for others who might look at the code in future.

Co-authored-by: Luciano Bello <[email protected]>

* Update state_preparation.py

incorporated Julien's changes!

* Update test_uniform_superposition_gate.py

Incorporated Julien's optimization suggestion.

* Update uniform-superposition-gate-3bd95ffdf05ef18c.yaml

implemented both reviewers' suggestions.

* Update uniform-superposition-gate-3bd95ffdf05ef18c.yaml

removed SV24

* Update state_preparation.py

* Update state_preparation.py

* Update test_uniform_superposition_gate.py

* Update uniform-superposition-gate-3bd95ffdf05ef18c.yaml

* Update qiskit/circuit/library/data_preparation/state_preparation.py

Co-authored-by: Julien Gacon <[email protected]>

* blacked state_preparation.py

* Update test_uniform_superposition_gate.py

* pylinted state_preparation.py

* removed transpile test_uniform_superposition_gate.py

---------

Co-authored-by: Julien Gacon <[email protected]>
Co-authored-by: Luciano Bello <[email protected]>
  • Loading branch information
3 people authored Jul 3, 2024
1 parent 0f585bd commit ba486b7
Show file tree
Hide file tree
Showing 5 changed files with 230 additions and 3 deletions.
11 changes: 9 additions & 2 deletions qiskit/circuit/library/data_preparation/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,14 @@
from .pauli_feature_map import PauliFeatureMap
from .z_feature_map import ZFeatureMap
from .zz_feature_map import ZZFeatureMap
from .state_preparation import StatePreparation
from .state_preparation import StatePreparation, UniformSuperpositionGate
from .initializer import Initialize

__all__ = ["PauliFeatureMap", "ZFeatureMap", "ZZFeatureMap", "StatePreparation", "Initialize"]
__all__ = [
"PauliFeatureMap",
"ZFeatureMap",
"ZZFeatureMap",
"StatePreparation",
"UniformSuperpositionGate",
"Initialize",
]
96 changes: 95 additions & 1 deletion qiskit/circuit/library/data_preparation/state_preparation.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@
from qiskit.circuit.library.standard_gates.s import SGate, SdgGate
from qiskit.circuit.library.generalized_gates import Isometry
from qiskit.circuit.exceptions import CircuitError
from qiskit.quantum_info.states.statevector import Statevector # pylint: disable=cyclic-import
from qiskit.quantum_info.states.statevector import (
Statevector,
) # pylint: disable=cyclic-import

_EPS = 1e-10 # global variable used to chop very small numbers to zero

Expand Down Expand Up @@ -240,3 +242,95 @@ def validate_parameter(self, parameter):

def _return_repeat(self, exponent: float) -> "Gate":
return Gate(name=f"{self.name}*{exponent}", num_qubits=self.num_qubits, params=[])


class UniformSuperpositionGate(Gate):
r"""Implements a uniform superposition state.
This gate is used to create the uniform superposition state
:math:`\frac{1}{\sqrt{M}} \sum_{j=0}^{M-1} |j\rangle` when it acts on an input
state :math:`|0...0\rangle`. Note, that `M` is not required to be
a power of 2, in which case the uniform superposition could be
prepared by a single layer of Hadamard gates.
.. note::
This class uses the Shukla-Vedula algorithm [1], which only needs
:math:`O(\log_2 (M))` qubits and :math:`O(\log_2 (M))` gates,
to prepare the superposition.
**References:**
[1]: A. Shukla and P. Vedula (2024), An efficient quantum algorithm for preparation
of uniform quantum superposition states, `Quantum Inf Process 23, 38
<https://link.springer.com/article/10.1007/s11128-024-04258-4>`_.
"""

def __init__(
self,
num_superpos_states: int = 2,
num_qubits: Optional[int] = None,
):
r"""
Args:
num_superpos_states (int):
A positive integer M = num_superpos_states (> 1) representing the number of computational
basis states with an amplitude of 1/sqrt(M) in the uniform superposition
state (:math:`\frac{1}{\sqrt{M}} \sum_{j=0}^{M-1} |j\rangle`, where
:math:`1< M <= 2^n`). Note that the remaining (:math:`2^n - M`) computational basis
states have zero amplitudes. Here M need not be an integer power of 2.
num_qubits (int):
A positive integer representing the number of qubits used. If num_qubits is None
or is not specified, then num_qubits is set to ceil(log2(num_superpos_states)).
Raises:
ValueError: num_qubits must be an integer greater than or equal to log2(num_superpos_states).
"""
if num_superpos_states <= 1:
raise ValueError("num_superpos_states must be a positive integer greater than 1.")
if num_qubits is None:
num_qubits = int(math.ceil(math.log2(num_superpos_states)))
else:
if not (isinstance(num_qubits, int) and (num_qubits >= math.log2(num_superpos_states))):
raise ValueError(
"num_qubits must be an integer greater than or equal to log2(num_superpos_states)."
)
super().__init__("USup", num_qubits, [num_superpos_states])

def _define(self):

qc = QuantumCircuit(self._num_qubits)

num_superpos_states = self.params[0]

if (
num_superpos_states & (num_superpos_states - 1)
) == 0: # if num_superpos_states is an integer power of 2
m = int(math.log2(num_superpos_states))
qc.h(range(m))
self.definition = qc
return

n_value = [int(x) for x in reversed(np.binary_repr(num_superpos_states))]
k = len(n_value)
l_value = [index for (index, item) in enumerate(n_value) if item == 1] # Locations of '1's

qc.x(l_value[1:k])
m_current_value = 2 ** l_value[0]
theta = -2 * np.arccos(np.sqrt(m_current_value / num_superpos_states))

if l_value[0] > 0: # if num_superpos_states is even
qc.h(range(l_value[0]))
qc.ry(theta, l_value[1])
qc.ch(l_value[1], range(l_value[0], l_value[1]), ctrl_state="0")

for m in range(1, len(l_value) - 1):
theta = -2 * np.arccos(
np.sqrt(2 ** l_value[m] / (num_superpos_states - m_current_value))
)
qc.cry(theta, l_value[m], l_value[m + 1], ctrl_state="0")
qc.ch(l_value[m + 1], range(l_value[m], l_value[m + 1]), ctrl_state="0")
m_current_value = m_current_value + 2 ** l_value[m]

self.definition = qc
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
features:
- |
Implemented :class:`.UniformSuperpositionGate` class, which allows
the creation of a uniform superposition state using
the Shukla-Vedula algorithm. This feature facilitates the
creation of quantum circuits that produce a uniform superposition
state :math:`\frac{1}{\sqrt{M}} \sum_{j=0}^{M-1} |j\rangle`, where
:math:`M` is a positive integer representing the number of
computational basis states with an amplitude of
:math:`\frac{1}{\sqrt{M}}`. This implementation supports the
efficient creation of uniform superposition states,
requiring only :math:`O(\log_2 (M))` qubits and
:math:`O(\log_2 (M))` gates. Usage example:
.. code-block:: python
from qiskit import QuantumCircuit
from qiskit.circuit.library.data_preparation import UniformSuperpositionGate
M = 5
num_qubits = 3
usp_gate = UniformSuperpositionGate(M, num_qubits)
qc = QuantumCircuit(num_qubits)
qc.append(usp_gate, list(range(num_qubits)))
qc.draw()
1 change: 1 addition & 0 deletions test/python/circuit/test_gate_definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,7 @@ class TestGateEquivalenceEqual(QiskitTestCase):
"ClassicalFunction",
"ClassicalElement",
"StatePreparation",
"UniformSuperpositionGate",
"LinearFunction",
"PermutationGate",
"Commuting2qBlock",
Expand Down
98 changes: 98 additions & 0 deletions test/python/circuit/test_uniform_superposition_gate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2024.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

"""
Uniform Superposition Gate test.
"""

import unittest
import math
from test import QiskitTestCase
import numpy as np
from ddt import ddt, data

from qiskit import QuantumCircuit
from qiskit.quantum_info import Operator, Statevector

from qiskit.circuit.library.data_preparation import (
UniformSuperpositionGate,
)


@ddt
class TestUniformSuperposition(QiskitTestCase):
"""Test initialization with UniformSuperpositionGate class"""

@data(2, 3, 5)
def test_uniform_superposition_gate(self, num_superpos_states):
"""Test Uniform Superposition Gate"""
n = int(math.ceil(math.log2(num_superpos_states)))
desired_sv = (1 / np.sqrt(num_superpos_states)) * np.array(
[1.0] * num_superpos_states + [0.0] * (2**n - num_superpos_states)
)
gate = UniformSuperpositionGate(num_superpos_states, n)
actual_sv = Statevector(gate)
np.testing.assert_allclose(desired_sv, actual_sv)

@data(2, 3, 5, 13)
def test_inverse_uniform_superposition_gate(self, num_superpos_states):
"""Test Inverse Uniform Superposition Gate"""
n = int(math.ceil(math.log2(num_superpos_states)))
gate = UniformSuperpositionGate(num_superpos_states, n)
qc = QuantumCircuit(n)
qc.append(gate, list(range(n)))
qc.append(gate.inverse(annotated=True), list(range(n)))
actual_unitary_matrix = np.array(Operator(qc).data)
desired_unitary_matrix = np.eye(2**n)
np.testing.assert_allclose(desired_unitary_matrix, actual_unitary_matrix, atol=1e-14)

@data(-2, -1, 0, 1)
def test_incompatible_num_superpos_states(self, num_superpos_states):
"""Test error raised if num_superpos_states not valid"""
n = 1
with self.assertRaises(ValueError):
UniformSuperpositionGate(num_superpos_states, n)

@data(1, 2, 3, 4)
def test_incompatible_int_num_superpos_states_and_qubit_args(self, n):
"""Test error raised if number of qubits not compatible with integer
state num_superpos_states (n >= log2(num_superpos_states) )"""
num_superpos_states = 50
with self.assertRaises(ValueError):
UniformSuperpositionGate(num_superpos_states, n)

@data(2, 3, 5)
def test_extra_qubits(self, num_superpos_states):
"""Tests for cases where n >= log2(num_superpos_states)"""
num_extra_qubits = 2
n = int(math.ceil(math.log2(num_superpos_states))) + num_extra_qubits
desired_sv = (1 / np.sqrt(num_superpos_states)) * np.array(
[1.0] * num_superpos_states + [0.0] * (2**n - num_superpos_states)
)
gate = UniformSuperpositionGate(num_superpos_states, n)
actual_sv = Statevector(gate)
np.testing.assert_allclose(desired_sv, actual_sv)

@data(2, 3, 5)
def test_no_qubit_args(self, num_superpos_states):
"""Test Uniform Superposition Gate without passing the number of qubits as an argument"""
n = int(math.ceil(math.log2(num_superpos_states)))
desired_sv = (1 / np.sqrt(num_superpos_states)) * np.array(
[1.0] * num_superpos_states + [0.0] * (2**n - num_superpos_states)
)
gate = UniformSuperpositionGate(num_superpos_states)
actual_sv = Statevector(gate)
np.testing.assert_allclose(desired_sv, actual_sv)


if __name__ == "__main__":
unittest.main()

0 comments on commit ba486b7

Please sign in to comment.