Skip to content

Commit

Permalink
Improvements to metods/documentation/testing for phase polynomials.
Browse files Browse the repository at this point in the history
  • Loading branch information
renezander90 committed Jun 13, 2024
1 parent 0bed3f6 commit 274918a
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 64 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
Phase polynomial tools
======================

These functions facilitate convenient and efficient application of diagonal Hamiltonians that are given by (multivariate) polynomials.
The application of such diagonal Hamiltonians can be utilized, e.g., as part of the :ref:`Quantum Approximate Optimization Algorithm <QAOA101>`:
In many cases the objective function of optimization problems is given by a polynomial. The phase separation operator within QAOA then requires
These functions facilitate convenient and efficient application of diagonal Hamiltonians that are given by polynomials.
That is, they implement transformations of the form

$$\\ket{x}\\rightarrow e^{itP(x)}\\ket{x}$$

where the Hamiltonian is given by a polynomial $P(x)$.

These functions can be utilized, e.g., as part of the :ref:`Quantum Approximate Optimization Algorithm <QAOA101>`:
In many cases the objective function of an optimization problem is given by a polynomial. The phase separation operator within QAOA then requires
the application of phases corresponding to the objective function values.

The application of general diagonal Hamiltonian functions can be achieved in the following ways:

- The first method is computing the Hamiltonian on all outcome labels of the input QuantumVariables, and subsequently applying the classically computed phases using logic synthesis.
While this facilitates a convenient application of arbitray diagonal Hamiltonian functions, it does not scale to large systems as all phases have to be computed classically.
- The :ref:`first method <DiagonalHamiltonianApplication>` consists of computing the Hamiltonian on all outcome labels of the input QuantumVariables, and subsequently applying the classically computed phases using logic synthesis. While this facilitates a convenient application of arbitray diagonal Hamiltonian functions, it does not scale to large systems as all phases have to be computed classically.

- The second method is evaluating the Hamiltonian on its input QuantumVariables into an auxiliary variable of type QuantumFloat, subsequently applying the phases on the auxiliary variable,
and finally uncomputing the auxiliary variable. This approch requires additional qubits and performing arithmetic on the quantum computer.
Therefore, on the one hand it poses an additional burden for simulators, and on the other hand is not applicable on NISQ devices.
- The second method is based on evaluating the Hamiltonian on its input QuantumVariables in superposition into an auxiliary variable of type QuantumFloat, subsequently applying the phases on the auxiliary variable, and finally uncomputing the auxiliary variable. This approch requires additional qubits and performing arithmetic on the quantum computer. Therefore, on the one hand it introduces an additional burden on simulators, and on the other hand is not suitable for NISQ devices.

In the special case where the Hamiltonian function is given by a polynomial, its application can be achieved by employing (multi-) controlled phase gates accordingly.
The following functions facilitate convenient and efficient application of such diagonal Hamiltonians that are given by (multivariate) polynomials.
The following functions facilitate convenient application of such diagonal Hamiltonians that are given by polynomials.

.. currentmodule:: qrisp

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
qrisp.app\_phase\_polynomial
============================

.. currentmodule:: qrisp

.. autofunction:: app_phase_polynomial
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
qrisp.app\_sb\_phase\_polynomial
================================

.. currentmodule:: qrisp

.. autofunction:: app_sb_phase_polynomial
105 changes: 52 additions & 53 deletions src/qrisp/arithmetic/SBP_arithmetic.py
Original file line number Diff line number Diff line change
Expand Up @@ -1113,19 +1113,18 @@ def quantum_bit_shift(qf, bit_shift, treat_overflow = True):
cyclic_shift(qf, bit_shift)


@lifted
def app_sb_phase_polynomial(input_qf_list, poly, symbol_list=None, t=1):
#@lifted
def app_sb_phase_polynomial(qv_list, poly, symbol_list=None, t=1):
"""
Applies a phase function specified by a (multivariate) SymPy polynomial on a list of QuantumVariables using
`semi-Boolean polynomials <https://ieeexplore.ieee.org/document/9815035>`_. That is, this method implements
the transformation
Applies a phase function specified by a `semi-Boolean polynomial <https://ieeexplore.ieee.org/document/9815035>`_ acting on a list of QuantumVariables.
That is, this method implements the transformation
.. math::
\ket{y_1}\dotsb\ket{y_n}\rightarrow e^{itP(y_1,\dotsc,y_n)}\ket{y_1}\dotsb\ket{y_n}
\ket{y_1}\dotsb\ket{y_n}\\rightarrow e^{itP(y_1,\dotsc,y_n)}\ket{y_1}\dotsb\ket{y_n}
where :math:`\ket{y_1},\dotsc,\ket{y_n}` are QuantumVariables and :math:`P(y_1,\dotsc,y_n)=P(y_{1,1},\dotsc,y_{1,m_1},\dotsc,y_{n,1}\dotsc,y_{n,m_n})` is a semi-Boolean polynomial in variables
:math:`y_{1,1},\dotsc,y_{1,m_1},\dotsc,y_{n,1}\dotsc,y_{n,m_n}`.
:math:`y_{1,1},\dotsc,y_{1,m_1},\dotsc,y_{n,1}\dotsc,y_{n,m_n}`. Here, $m_i$ is the size of the $i$ th variable.
Parameters
----------
Expand All @@ -1134,12 +1133,12 @@ def app_sb_phase_polynomial(input_qf_list, poly, symbol_list=None, t=1):
poly : SymPy expression
The semi-Boolean polynomial to evaluate.
symbol_list : list, optional
An ordered list of SymPy symbols associated to the qubits of the QuantumVariables of ''qv_list''.
For each QuantumVariable in ''qv_list'' a number of symbols according to its size is required.
An ordered list of SymPy symbols associated to the qubits of the QuantumVariables of ``qv_list``.
For each QuantumVariable in ``qv_list`` a number of symbols according to its size is required.
By default, the symbols of the polynomial
will be ordered alphabetically and then matched to the order in ''qv_list''.
will be ordered alphabetically and then matched to the order in ``qv_list``.
t : Float or SymPy expression, optional
The argument ''t'' in the expression $\exp(itP)$.
The argument ``t`` in the expression $\exp(itP)$.
Raises
------
Expand All @@ -1150,30 +1149,31 @@ def app_sb_phase_polynomial(input_qf_list, poly, symbol_list=None, t=1):
Examples
--------
We apply the phase function specified by the polynomial :math:`\pi xyz` on a QuantumVariable:
We apply the phase function specified by the polynomial :math:`P(x,y,z) = \pi xyz` on a QuantumVariable:
::
import sympy as sp
import numpy as np
from qrisp import QuantumVariable, app_sb_phase_polynomial
qv = QuantumVariable(3)
qv.init_state({'000': 0.5, '111': 0.5})
x, y, z = sp.symbols('x y z')
P = np.pi*x*y*z
qv = QuantumVariable(3)
qv.init_state({'000': 0.5, '111': 0.5})
app_sb_phase_polynomial([qv], P)
::
We print the ``statevector``:
>>>qv.qs.statevector()
:math:`\frac{\sqrt{2}}{2}(\ket{000}-\ket{111})`
>>> print(qv.qs.statevector())
sqrt(2)*(|000> - |111>)/2
"""

if isinstance(input_qf_list, QuantumArray):
input_qf_list = list(input_qf_list.flatten())
if isinstance(qv_list, QuantumArray):
qv_list = list(qv_list.flatten())

# As the polynomial has only boolean variables,
# powers can be ignored since x**k = x for x in GF(2)
Expand All @@ -1182,14 +1182,14 @@ def app_sb_phase_polynomial(input_qf_list, poly, symbol_list=None, t=1):
if symbol_list is None:
symbol_list = get_ordered_symbol_list(poly)

if len(symbol_list) != sum([var.size for var in input_qf_list]):
if len(symbol_list) != sum([var.size for var in qv_list]):
raise Exception(
"Provided QuantumVariable list does not include the appropriate amount "
"of elements to evaluate the given polynomial"
)

# The list of qubits contained in the variables of input_var_list
input_qubits = sum([list(var.reg) for var in input_qf_list], [])
input_qubits = sum([list(var.reg) for var in qv_list], [])

# Monomials in list form
monomial_list = expr_to_list(poly)
Expand Down Expand Up @@ -1240,32 +1240,31 @@ def app_sb_phase_polynomial(input_qf_list, poly, symbol_list=None, t=1):
gphase(y*t,input_qubits[0])


@lifted
#@lifted
def app_phase_polynomial(qf_list, poly, symbol_list=None, t=1):
"""
Applies a phase function specified by a (multivariate) SymPy polynomial on a list of QuantumFloats using
`semi-boolean polynomials <https://ieeexplore.ieee.org/document/9815035>`_. That is, this method implements
the transformation
Applies a phase function specified by a polynomial acting on a list of QuantumFloats.
That is, this method implements the transformation
.. math::
\ket{y_1}\dotsb\ket{y_n}\rightarrow e^{itP(y_1,\dotsc,y_n)}\ket{y_1}\dotsb\ket{y_n}
\ket{y_1}\dotsb\ket{y_n}\\rightarrow e^{itP(y_1,\dotsc,y_n)}\ket{y_1}\dotsb\ket{y_n}
where :math:`\ket{y_1},\dotsc,\ket{y_n}` are QuantumFloats and :math:`P(y_1,\dotsc,y_n)` is a (multivariate) polynomial in variables
where :math:`\ket{y_1},\dotsc,\ket{y_n}` are QuantumFloats and :math:`P(y_1,\dotsc,y_n)` is a polynomial in variables
:math:`y_1,\dotsc,y_n`.
Parameters
----------
qf_list : list[QuantumFloat] or QuantumArray
qf_list : list[QuantumFloat] or QuantumArray[QuantumFloat]
The list of QuantumFloats to evaluate the polynomial on.
poly : sympy expression
The polynomial to evaluate.
symbol_list : dict, optional
An ordered list of SymPy symbols associated to the QuantumFloats of ''qf_list''.
symbol_list : list, optional
An ordered list of SymPy symbols associated to the QuantumFloats of ``qf_list``.
By default, the symbols of the polynomial
will be ordered alphabetically and then matched to the order in ''qf_list''.
will be ordered alphabetically and then matched to the order in ``qf_list``.
t : Float or SymPy expression, optional
The argument ''t'' in the expression $\exp(itP)$. The default is 1.
The argument ``t`` in the expression $\exp(itP)$. The default is 1.
Raises
------
Expand All @@ -1284,61 +1283,58 @@ def app_phase_polynomial(qf_list, poly, symbol_list=None, t=1):
import numpy as np
from qrisp import QuantumFloat, h, app_phase_polynomial
x, y = sp.symbols('x y')
P = np.pi*x + np.pi*x*y
qf1 = QuantumFloat(3, signed = False)
qf2 = QuantumFloat(3,-1, signed = False)
h(qf1[0])
qf2[:]=0.5
x=sp.Symbol('x')
y=sp.Symbol('y')
P = np.pi*x + np.pi*x*y
app_phase_polynomial([qf1,qf2], P)
::
>>>qf1.qs.statevector()
:math:`\frac{\sqrt{2}}{2}(\ket{0}\ket{0.5}-i\ket{1}\ket{0.5})`
We print the ``statevector``:
>>> print(qf1.qs.statevector())
sqrt(2)*(|0>*|0.5> - I*|1>*|0.5>)/2
We apply the phase function specified by the polynomial :math:`P(x) = 1 - 0.9x^2 + x^3` on a QuantumFloat:
::
import numpy as np
import sympy as sp
import matplotlib.pyplot as plt
from qrisp import QuantumFloat, h, app_phase_polynomial
x = sp.symbols('x')
P = 1-0.9*x**2+x**3
qf = QuantumFloat(3,-3)
h(qf)
x = sp.symbols('x')
P = 1-0.9*x**2+x**3
app_phase_polynomial([qf],P)
::
To visualize the results we retrieve the ''statevector'' as a function and determine the phase of each entry.
To visualize the results we retrieve the ``statevector`` as a function and determine the phase of each entry.
::
sv_function = qf.qs.statevector("function")
::
This function receives a dictionary of QuantumVariables specifiying the desired label constellation and returns its complex amplitude.
We calculate the phases corresponding to the complex amplitudes, and compare the results with the values of the function $P(x)$.
This function receives a dictionary of QuantumVariables specifiying the desired label constellation and returns its complex amplitude.
We calculate the phases corresponding to the complex amplitudes, and compare the results with the values of the function $P(x)$.
::
qf_values = np.array([qf.decoder(i) for i in range(2 ** qf.size)])
sv_phase_array = np.angle([sv_function({qf : i}) for i in qf_values])
P_func = sp.lambdify(x, P, 'numpy')
x_values = np.linspace(0, 1, 100)
y_values = P_func(x_values)
::
Finally, we plot the results.
Finally, we plot the results.
::
Expand All @@ -1350,7 +1346,10 @@ def app_phase_polynomial(qf_list, poly, symbol_list=None, t=1):
plt.legend()
plt.show()
::
.. figure:: /_static/PhasePolynomialApplication.png
:alt: PhasePolynomialApplication
:scale: 80%
:align: center
"""

Expand Down
5 changes: 3 additions & 2 deletions tests/test_diagonal_hamiltonian_application.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,11 +138,12 @@ def test_phase_polynomial_application():
from qrisp import QuantumFloat, h, app_phase_polynomial

# We apply the phase function specified by the polynomial P(x) = 1 - 0.9x^2 + x^3 on a QuantumFloat
x = sp.symbols('x')
P = 1-0.9*x**2+x**3

qf = QuantumFloat(3,-3)
h(qf)

x = sp.symbols('x')
P = 1-0.9*x**2+x**3
app_phase_polynomial([qf],P)

sv_function = qf.qs.statevector("function")
Expand Down

0 comments on commit 274918a

Please sign in to comment.