Source code for qolumbina.programs.state_preparation.graph_state
# The original version of the following code is sourced from Qiskit circuit library:
# https://github.com/Qiskit/qiskit/blob/stable/2.3/qiskit/circuit/library/graph_state.py#L89-L173
#
# Original repository link:
# https://github.com/Qiskit/qiskit/tree/f14e0b29a484795034447ea5bfb637fe845c194f
#
# This program is adapted for use as a benchmark in controlled software testing experiments.
# No modifications in functional changes were made to the original code.
#
# This code is part of Qiskit.
#
# (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
# 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.
"""Graph State circuit and gate."""
from __future__ import annotations
import numpy as np
from qiskit.circuit.quantumcircuit import QuantumCircuit, Gate
from qiskit.circuit.exceptions import CircuitError
# from qiskit.utils.deprecation import deprecate_func
# ---------- benchmark registration ----------
from ..benchmark_registry import register_benchmark
from pathlib import Path
@register_benchmark(
Path(__file__).stem,
family=Path(__file__).resolve().parent.name,
description="Prepare a graph state based on an adjacency matrix",
class_name="GraphState",
source={
"repo": "https://github.com/Qiskit/qiskit/tree/f14e0b29a484795034447ea5bfb637fe845c194f",
"file": "qiskit/circuit/library/graph_state.py",
"sdk": "Qiskit",
"available_doc": True
},
testability_refactoring=None,
)
def create_graph_state(adjacency_matrix: list | np.ndarray) -> QuantumCircuit:
return GraphState(adjacency_matrix=adjacency_matrix)
[docs]
class GraphState(QuantumCircuit):
r"""Circuit to prepare 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::
:alt: Diagram illustrating the previously described circuit.
from qiskit.circuit.library import GraphState
from qiskit.visualization.library import _generate_circuit_library_visualization
import rustworkx as rx
G = rx.generators.cycle_graph(5)
circuit = GraphState(rx.adjacency_matrix(G))
circuit.name = "Graph state"
_generate_circuit_library_visualization(circuit)
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>`_
"""
# @deprecate_func(
# since="2.1",
# additional_msg="Use qiskit.circuit.library.GraphStateGate instead.",
# removal_timeline="in Qiskit 3.0",
# )
def __init__(self, adjacency_matrix: list | np.ndarray) -> None:
r"""Create graph state preparation circuit.
Args:
adjacency_matrix: Adjacency matrix representing the graph, which is presented
in a form of :math:`n`-by-:math:`n` list of 0-1 lists.
Raises:
CircuitError: If adjacency_matrix is not symmetric.
The circuit prepares 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.")
graph_state_gate = GraphStateGate(adjacency_matrix)
super().__init__(graph_state_gate.num_qubits)
self.compose(graph_state_gate, range(graph_state_gate.num_qubits), inplace=True)
# Automatically decompose for simulator compatibility
self._decompose_for_simulator()
def _decompose_for_simulator(self):
decomposed_circuit = self.decompose()
self.data = decomposed_circuit.data
[docs]
class GraphStateGate(Gate):
r"""A gate representing a graph state.
"""
def __init__(self, adjacency_matrix: list | np.ndarray) -> None:
r"""
Args:
adjacency_matrix: Adjacency matrix representing the graph,
which is presented in a form of :math:`n`-by-:math:`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)
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)
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)
self.definition = circuit
[docs]
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)
)