Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More quantum circuit library refactoring #13353

Merged
merged 20 commits into from
Nov 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
018d654
GraphState -> GraphStateGate
alexanderivrii Oct 22, 2024
24451d7
adding GraphStateGate::__eq__ method
alexanderivrii Oct 22, 2024
959f86c
oops... fourier checking should not be a gate
alexanderivrii Oct 22, 2024
266c636
FourierChecking -> fourier_checking
alexanderivrii Oct 22, 2024
9061e17
UnitaryOverlap -> unitary_overlap
alexanderivrii Oct 22, 2024
0b56370
visualization improvements (following quantum_volume code)
alexanderivrii Oct 22, 2024
7ee7582
visualization improvements (following quantum_volume code)
alexanderivrii Oct 22, 2024
edf9c03
HiddenLinearFunction -> hidden_linear_function
alexanderivrii Oct 22, 2024
40ecddc
unused imports
alexanderivrii Oct 22, 2024
a949b2b
PhaseEstimation -> phase_estimation
alexanderivrii Oct 22, 2024
d7fe4ac
cleaning up phase estimation code
alexanderivrii Oct 22, 2024
6af453f
reno
alexanderivrii Oct 22, 2024
30c85e0
Merge branch 'main' into finalize_modernize
alexanderivrii Nov 3, 2024
f628712
Restoring to the original definition of the FourierChecking circuit
alexanderivrii Nov 3, 2024
44d48e9
Merge branch 'finalize_modernize' of github.com:alexanderivrii/qiskit…
alexanderivrii Nov 3, 2024
4618f08
pass over fourier_checking function
alexanderivrii Nov 3, 2024
3c7f929
remaining suggestions from code review; missing tests; missing API refs
alexanderivrii Nov 3, 2024
7ad342b
pylint
alexanderivrii Nov 3, 2024
61731b9
reno fix
alexanderivrii Nov 3, 2024
36e3450
another small round of addressing review comments
alexanderivrii Nov 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 14 additions & 5 deletions qiskit/circuit/library/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -321,20 +321,29 @@
:template: autosummary/class_no_inherited_members.rst

FourierChecking
fourier_checking
GraphState
GraphStateGate
HiddenLinearFunction
hidden_linear_function
IQP
QuantumVolume
quantum_volume
PhaseEstimation
phase_estimation
GroverOperator
PhaseOracle
PauliEvolutionGate
HamiltonianGate
UnitaryOverlap
unitary_overlap

.. autofunction:: iqp
.. autofunction:: random_iqp
.. autofunction:: fourier_checking
.. autofunction:: hidden_linear_function
.. autofunction:: unitary_overlap
.. autofunction:: phase_estimation


N-local circuits
Expand Down Expand Up @@ -582,11 +591,11 @@
Initialize,
)
from .quantum_volume import QuantumVolume, quantum_volume
from .fourier_checking import FourierChecking
from .graph_state import GraphState
from .hidden_linear_function import HiddenLinearFunction
from .fourier_checking import FourierChecking, fourier_checking
from .graph_state import GraphState, GraphStateGate
from .hidden_linear_function import HiddenLinearFunction, hidden_linear_function
from .iqp import IQP, iqp, random_iqp
from .phase_estimation import PhaseEstimation
from .phase_estimation import PhaseEstimation, phase_estimation
from .grover_operator import GroverOperator
from .phase_oracle import PhaseOracle
from .overlap import UnitaryOverlap
from .overlap import UnitaryOverlap, unitary_overlap
83 changes: 72 additions & 11 deletions qiskit/circuit/library/fourier_checking.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@

"""Fourier checking circuit."""

from typing import List

from collections.abc import Sequence
import math

from qiskit.circuit import QuantumCircuit
from qiskit.circuit.exceptions import CircuitError
from qiskit.utils.deprecation import deprecate_func

from .generalized_gates.diagonal import Diagonal
from .generalized_gates.diagonal import Diagonal, DiagonalGate


class FourierChecking(QuantumCircuit):
Expand Down Expand Up @@ -52,7 +53,12 @@ class FourierChecking(QuantumCircuit):
`arXiv:1411.5729 <https://arxiv.org/abs/1411.5729>`_
"""

def __init__(self, f: List[int], g: List[int]) -> None:
@deprecate_func(
since="1.3",
additional_msg="Use qiskit.circuit.library.fourier_checking instead.",
pending=True,
)
def __init__(self, f: Sequence[int], g: Sequence[int]) -> None:
"""Create Fourier checking circuit.

Args:
Expand Down Expand Up @@ -81,17 +87,72 @@ def __init__(self, f: List[int], g: List[int]) -> None:
"{1, -1}."
)

circuit = QuantumCircuit(num_qubits, name=f"fc: {f}, {g}")

# This definition circuit is not replaced by the circuit produced by fourier_checking,
# as the latter produces a slightly different circuit, with DiagonalGates instead
# of Diagonal circuits.
circuit = QuantumCircuit(int(num_qubits), name=f"fc: {f}, {g}")
circuit.h(circuit.qubits)

circuit.compose(Diagonal(f), inplace=True)

circuit.h(circuit.qubits)

circuit.compose(Diagonal(g), inplace=True)

circuit.h(circuit.qubits)

super().__init__(*circuit.qregs, name=circuit.name)
self.compose(circuit.to_gate(), qubits=self.qubits, inplace=True)


def fourier_checking(f: Sequence[int], g: Sequence[int]) -> QuantumCircuit:
"""Fourier checking circuit.

The circuit for the Fourier checking algorithm, introduced in [1],
involves a layer of Hadamards, the function :math:`f`, another layer of
Hadamards, the function :math:`g`, followed by a final layer of Hadamards.
The functions :math:`f` and :math:`g` are classical functions realized
as phase oracles (diagonal operators with {-1, 1} on the diagonal).

The probability of observing the all-zeros string is :math:`p(f,g)`.
The algorithm solves the promise Fourier checking problem,
which decides if f is correlated with the Fourier transform
of g, by testing if :math:`p(f,g) <= 0.01` or :math:`p(f,g) >= 0.05`,
promised that one or the other of these is true.

The functions :math:`f` and :math:`g` are currently implemented
from their truth tables but could be represented concisely and
implemented efficiently for special classes of functions.

Fourier checking is a special case of :math:`k`-fold forrelation [2].

**Reference Circuit:**

.. plot::
:include-source:

from qiskit.circuit.library import fourier_checking
circuit = fourier_checking([1, -1, -1, -1], [1, 1, -1, -1])
circuit.draw('mpl')

**Reference:**

[1] S. Aaronson, BQP and the Polynomial Hierarchy, 2009 (Section 3.2).
`arXiv:0910.4698 <https://arxiv.org/abs/0910.4698>`_

[2] S. Aaronson, A. Ambainis, Forrelation: a problem that
optimally separates quantum from classical computing, 2014.
`arXiv:1411.5729 <https://arxiv.org/abs/1411.5729>`_
"""
num_qubits = math.log2(len(f))

if len(f) != len(g) or num_qubits == 0 or not num_qubits.is_integer():
raise CircuitError(
"The functions f and g must be given as truth "
"tables, each as a list of 2**n entries of "
"{1, -1}."
)
num_qubits = int(num_qubits)

circuit = QuantumCircuit(num_qubits, name=f"fc: {f}, {g}")
circuit.h(circuit.qubits)
circuit.append(DiagonalGate(f), range(num_qubits))
circuit.h(circuit.qubits)
circuit.append(DiagonalGate(g), range(num_qubits))
circuit.h(circuit.qubits)
return circuit
103 changes: 93 additions & 10 deletions qiskit/circuit/library/graph_state.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2020.
# (C) Copyright IBM 2017, 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
Expand All @@ -10,13 +10,14 @@
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

"""Graph State circuit."""
"""Graph State circuit and gate."""

from __future__ import annotations

import numpy as np
from qiskit.circuit.quantumcircuit import QuantumCircuit
from qiskit.circuit.quantumcircuit import QuantumCircuit, Gate
from qiskit.circuit.exceptions import CircuitError
from qiskit.utils.deprecation import deprecate_func


class GraphState(QuantumCircuit):
Expand Down Expand Up @@ -56,6 +57,11 @@ class GraphState(QuantumCircuit):
`arXiv:1512.07892 <https://arxiv.org/pdf/1512.07892.pdf>`_
"""

@deprecate_func(
since="1.3",
additional_msg="Use qiskit.circuit.library.GraphStateGate instead.",
pending=True,
)
def __init__(self, adjacency_matrix: list | np.ndarray) -> None:
"""Create graph state preparation circuit.

Expand All @@ -73,14 +79,91 @@ def __init__(self, adjacency_matrix: list | np.ndarray) -> None:
if not np.allclose(adjacency_matrix, adjacency_matrix.transpose()):
raise CircuitError("The adjacency matrix must be symmetric.")

graph_state_gate = GraphStateGate(adjacency_matrix)
super().__init__(graph_state_gate.num_qubits, name=f"graph: {adjacency_matrix}")
self.compose(graph_state_gate, qubits=self.qubits, inplace=True)


class GraphStateGate(Gate):
r"""A gate representing a graph state.

Given a graph G = (V, E), with the set of vertices V and the set of edges E,
the corresponding graph state is defined as

.. math::

|G\rangle = \prod_{(a,b) \in E} CZ_{(a,b)} {|+\rangle}^{\otimes V}

Such a state can be prepared by first preparing all qubits in the :math:`+`
state, then applying a :math:`CZ` gate for each corresponding graph edge.

Graph state preparation circuits are Clifford circuits, and thus
easy to simulate classically. However, by adding a layer of measurements
in a product basis at the end, there is evidence that the circuit becomes
hard to simulate [2].

**Reference Circuit:**

.. plot::
:include-source:

from qiskit.circuit import QuantumCircuit
from qiskit.circuit.library import GraphStateGate
import rustworkx as rx

G = rx.generators.cycle_graph(5)
circuit = QuantumCircuit(5)
circuit.append(GraphStateGate(rx.adjacency_matrix(G)), [0, 1, 2, 3, 4])
circuit.decompose().draw('mpl')

**References:**

[1] M. Hein, J. Eisert, H.J. Briegel, Multi-party Entanglement in Graph States,
`arXiv:0307130 <https://arxiv.org/pdf/quant-ph/0307130.pdf>`_
[2] D. Koh, Further Extensions of Clifford Circuits & their Classical Simulation Complexities.
`arXiv:1512.07892 <https://arxiv.org/pdf/1512.07892.pdf>`_
"""

def __init__(self, adjacency_matrix: list | np.ndarray) -> None:
"""
Args:
adjacency_matrix: input graph as n-by-n list of 0-1 lists

Raises:
CircuitError: If adjacency_matrix is not symmetric.

The gate represents a graph state with the given adjacency matrix.
"""

adjacency_matrix = np.asarray(adjacency_matrix)
if not np.allclose(adjacency_matrix, adjacency_matrix.transpose()):
raise CircuitError("The adjacency matrix must be symmetric.")
num_qubits = len(adjacency_matrix)
circuit = QuantumCircuit(num_qubits, name=f"graph: {adjacency_matrix}")

circuit.h(range(num_qubits))
for i in range(num_qubits):
for j in range(i + 1, num_qubits):
super().__init__(name="graph_state", num_qubits=num_qubits, params=[adjacency_matrix])

def _define(self):
adjacency_matrix = self.adjacency_matrix
circuit = QuantumCircuit(self.num_qubits, name=self.name)
circuit.h(range(self.num_qubits))
for i in range(self.num_qubits):
for j in range(i + 1, self.num_qubits):
if adjacency_matrix[i][j] == 1:
circuit.cz(i, j)

super().__init__(*circuit.qregs, name=circuit.name)
self.compose(circuit.to_gate(), qubits=self.qubits, inplace=True)
self.definition = circuit

def validate_parameter(self, parameter):
"""Parameter validation"""
return parameter

@property
def adjacency_matrix(self):
"""Returns the adjacency matrix."""
return self.params[0]

def __eq__(self, other):
return (
isinstance(other, GraphStateGate)
and self.num_qubits == other.num_qubits
and np.all(self.adjacency_matrix == other.adjacency_matrix)
)
Loading