1. Quantum states in GraphiQ¶
1.1 Overview¶
In GraphiQ, quantum states are represented as a QuantumState object. This object contains one or more state representations -- density matrix representations; in the future, this class is also expected to mediate conversions between different state representations.
Our quantum states have 3 currently planned representations: 1. Density matrix: full representation of a quantum state, matrix size scales exponentially with number of qubits. 2. Stabilizer: representation of quantum states using the stabilizer formalism, which restricts what states can be modelled, but circuit simulation runtime is polynomial in the number of qubits. 3. Graph: graph-based representation of quantum states, restricted to so-called "graph states". This representation is more restrictive than the stabilizer, but enables the use of graph-theoretic tools.
1.2 Density matrix¶
Internally, the density matrix is represented by a numpy array. Additionally, the class contains helper functions which allows us to apply quantum operations (e.g. unitary gates, measurements) or noise to our system without manually modifying the numpy array.
1.2.1 Initializing DensityMatrix state representation¶
Most often, the only direct state manipulation a user will have to perform is the initialization of their ideal state (the remaining state manipulation occurs during the compilation/simulation of the circuit, which is done automatically).
""" Initializing a density matrix from a numpy array """
# We can initialize it from any defined numpy array of adequate dimensions
import numpy as np
n = 3
data = np.eye(2**n) # this generates an identity matrix for an n-qubit state
dm_state = DensityMatrix(data)
dm_state.draw()
print(dm_state.data)
""" Initializing a state with density matrix helper functions """
# Helper functions for density matrix operations
import graphiq.backends.density_matrix.functions as dmf
# 4-qubit GHZ state: |phi> = 1/sqrt(2) * (|0000> + |1111>)
ghz4_dm = dmf.ket2dm(
(dmf.tensor(4 * [dmf.state_ketz0()]) + dmf.tensor(4 * [dmf.state_ketz1()]))
/ np.sqrt(2)
)
ghz4_state = DensityMatrix(ghz4_dm)
ghz4_state.draw()
print(dm_state.data)
"""
Defining a target state as a graph and converting to a density matrix
"""
import networkx as nx
cluster_state = nx.Graph(
[(1, 2), (2, 3)]
) # add graph edges between node 1-2, node 2-3 -- a linear chain
nx.draw(cluster_state)
dm = DensityMatrix.from_graph(cluster_state)
dm.draw()
print(dm.data)
1.2.2 Density matrix functions¶
# We can get gates from the density matrix helper functions
#
h_gate = dmf.get_one_qubit_gate(
n, # number of total qubits in the state
0, # index of the density matrix which represents the target qubit
dmf.hadamard(), # the single-qubit state version of the gate we want to apply
)
dm_state.apply_unitary(h_gate)
dm_state.draw();
""" Measure a qubit (projects the qubit onto a given basis--we will focus on the computational basis Z)"""
# We measure qubit to convert the (inaccessible to us) quantum information to classical information
projectors = dmf.projectors_zbasis(n, 0) # this is the basis on which we project
outcome = dm_state.apply_measurement(
projectors, # the projector
measurement_determinism="probabilistic" # most quantum measurements are inherently probabilistic,
# but we allow deterministic measurements for algorithmic reasons
)
print(f"Measurement outcome: {outcome}")
dm_state.draw();
1.3 Stabilizer representation¶
An \(n\)-qubit stabilizer state is represented with 4 tableaus (a binary matrix): - Destabilizer: \(n\) vector - Stabilizer: \(n\) vector - phase: \(2n\) vector - \(i\) phase: \(2n\) vector
"""
Defining a target state from a graph by converting the target graph to a stabilizer
"""
import networkx as nx
from graphiq.backends.stabilizer.functions.rep_conversion import (
get_clifford_tableau_from_graph,
)
cluster_state = nx.Graph([(1, 2), (2, 3)]) # Edge between node 1-2, node 2-3
nx.draw(cluster_state)
linear_cluster_3_stabilizer_state = get_clifford_tableau_from_graph(cluster_state)
print(linear_cluster_3_stabilizer_state)
1.3.1 Stabilizer functions¶
1.4 Graph state representation [in development]¶
1.5 Unified interface: QuantumState objects¶
The QuantumState object wraps different state formalisms/representations into one object, and provides a simple interface to define, manipulate, and visualize the state across representations.
1.6 Conversion between state representations¶
Not all representations can be converted to one another without loss of information. * The Graph representation can represent only a class of quantum states called graph states, and is the least general. * The Stabilizer representation is more general, and can represent any "pure states" (there are no unknowns associated with classical probabilities). * The DensityMatrix representation is most general of all, and can represent both pure states and mixed states.
""" Graph to density matrix"""
from graphiq.backends.state_rep_conversion import (
density_to_graph,
graph_to_density,
graph_to_stabilizer,
stabilizer_to_graph,
stabilizer_to_density,
density_to_stabilizer,
)
import graphiq.visualizers.density_matrix as vdm
linear3_g = nx.Graph([(1, 2), (2, 3)]) # Edge between node 1-2, node 2-3
linear3_dm = graph_to_density(linear3_g)
nx.draw(linear3_g)
vdm.density_matrix_bars(linear3_dm);
""" Density matrix to graph: exact """
# we know that linear3_dm is a graph state--we can convert it back to a graph
graph_list = density_to_graph(linear3_dm, threshold=0.4999)
# as linear3_dm is a pure state, the graph_list should contain only one element
linear3_graph_from_dm = nx.from_numpy_array(graph_list)
nx.draw(linear3_graph_from_dm);
""" Stabilizer to density and back """
linear3_dm_from_stabilizer = stabilizer_to_density(linear3_stabilizer)
print(linear3_dm_from_stabilizer)
# vdm.density_matrix_bars(linear3_dm_from_stabilizer.data)
# linear3_stabilizer_from_dm = density_to_stabilizer(linear3_dm_from_stabilizer)
# print(linear3_stabilizer_from_dm)