Skip to content

Commit

Permalink
Merge pull request #1165 from qiboteam/basis_encoding
Browse files Browse the repository at this point in the history
Add `comp_basis_encoder` to `models.encodings`
  • Loading branch information
renatomello authored Feb 2, 2024
2 parents 51a0e92 + bfb3ae3 commit 7585775
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 1 deletion.
26 changes: 26 additions & 0 deletions doc/source/api-reference/qibo.rst
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,32 @@ Data Encoders

We provide a family of algorithms that encode classical data into quantum circuits.

Computational Basis Encoder
"""""""""""""""""""""""""""

Given a bitstring :math:`b` of length :math:`n`, this encoder generates a layer of Pauli-:math:`X`
gates that creates the quantum state :math:`|\,b\,\rangle`.

For instance, the following two circuit generations are equivalent:

.. testsetup::

from qibo import Circuit, gates
from qibo.models.encodings import comp_basis_encoder

.. testcode::

b = "101"
circuit_1 = comp_basis_encoder(b)

circuit_2 = Circuit(3)
circuit_2.add(gates.X(0))
circuit_2.add(gates.X(2))


.. autofunction:: qibo.models.encodings.comp_basis_encoder


Unary Encoder
"""""""""""""

Expand Down
60 changes: 60 additions & 0 deletions src/qibo/models/encodings.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Module with functions that encode classical data into quantum circuits."""

import math
from typing import Optional, Union

import numpy as np
from scipy.stats import rv_continuous
Expand All @@ -10,6 +11,65 @@
from qibo.models.circuit import Circuit


def comp_basis_encoder(
basis_element: Union[int, str, list, tuple], nqubits: Optional[int] = None
):
"""Creates circuit that performs encoding of bitstrings into computational basis states.
Args:
basis_element (int or str or list or tuple): bitstring to be encoded.
If ``int``, ``nqubits`` must be specified.
If ``str``, must be composed of only :math:`0`s and :math:`1`s.
If ``list`` or ``tuple``, must be composed of :math:`0`s and
:math:`1`s as ``int`` or ``str``.
nqubits (int, optional): total number of qubits in the circuit.
If ``basis_element`` is ``int``, ``nqubits`` must be specified.
If ``nqubits`` is ``None``, ``nqubits`` defaults to length of ``basis_element``.
Defaults to ``None``.
Returns:
:class:`qibo.models.circuit.Circuit`: circuit encoding computational basis element.
"""
if not isinstance(basis_element, (int, str, list, tuple)):
raise_error(
TypeError,
"basis_element must be either type int or str or list or tuple, "
+ f"but it is type {type(basis_element)}.",
)

if isinstance(basis_element, (str, list, tuple)):
if any(elem not in ["0", "1", 0, 1] for elem in basis_element):
raise_error(ValueError, "all elements must be 0 or 1.")

if nqubits is not None and not isinstance(nqubits, int):
raise_error(
TypeError, f"nqubits must be type int, but it is type {type(nqubits)}."
)

if nqubits is None:
if isinstance(basis_element, int):
raise_error(
ValueError, f"nqubits must be specified when basis_element is type int."
)
else:
nqubits = len(basis_element)

if isinstance(basis_element, int):
basis_element = f"{basis_element:0{nqubits}b}"

if isinstance(basis_element, (str, tuple)):
basis_element = list(basis_element)

basis_element = list(map(int, basis_element))

circuit = Circuit(nqubits)
for qubit, elem in enumerate(basis_element):
if elem == 1:
circuit.add(gates.X(qubit))

return circuit


def unary_encoder(data, architecture: str = "tree"):
"""Creates circuit that performs the unary encoding of ``data``.
Expand Down
36 changes: 35 additions & 1 deletion tests/test_models_encodings.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,48 @@
import pytest
from scipy.optimize import curve_fit

from qibo.models.encodings import unary_encoder, unary_encoder_random_gaussian
from qibo.models.encodings import (
comp_basis_encoder,
unary_encoder,
unary_encoder_random_gaussian,
)


def gaussian(x, a, b, c):
"""Gaussian used in the `unary_encoder_random_gaussian test"""
return np.exp(a * x**2 + b * x + c)


@pytest.mark.parametrize(
"basis_element", [5, "101", ["1", "0", "1"], [1, 0, 1], ("1", "0", "1"), (1, 0, 1)]
)
def test_comp_basis_encoder(backend, basis_element):
with pytest.raises(TypeError):
circuit = comp_basis_encoder(2.3)
with pytest.raises(ValueError):
circuit = comp_basis_encoder("0b001")
with pytest.raises(ValueError):
circuit = comp_basis_encoder("001", nqubits=2)
with pytest.raises(TypeError):
circuit = comp_basis_encoder("001", nqubits=3.1)
with pytest.raises(ValueError):
circuit = comp_basis_encoder(3)

zero = np.array([1, 0], dtype=complex)
one = np.array([0, 1], dtype=complex)
target = np.kron(one, np.kron(zero, one))
target = backend.cast(target, dtype=target.dtype)

if isinstance(basis_element, int):
state = comp_basis_encoder(basis_element, nqubits=3)
else:
state = comp_basis_encoder(basis_element)

state = backend.execute_circuit(state).state()

backend.assert_allclose(state, target)


@pytest.mark.parametrize("architecture", ["tree", "diagonal"])
@pytest.mark.parametrize("nqubits", [8])
def test_unary_encoder(backend, nqubits, architecture):
Expand Down

0 comments on commit 7585775

Please sign in to comment.