1. Utilities¶
1.1 State representation conversion¶
graphiq.backends.state_rep_conversion ¶
Convert between different representations of quantum states; including density matrices, graph, and stabilizer.
density_to_graph(input_matrix, threshold=0.1, validate=True) ¶
Converts a density matrix state representation to a graph representation via an adjacency matrix. It assumes qubit systems.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
input_matrix | numpy.ndarray | a density matrix to be converted to a graph | required |
threshold | double | float | a minimum threshold value of negativity for the state to assume entangled | 0.1 |
validate | bool | validate the input density is a graph state if True | True |
Returns:
| Type | Description |
|---|---|
numpy.ndarray | list[(float, numpy.ndarray)] | an adjacency matrix representation for a pure state or a list of adjacency matrices for a mixed state |
Raises:
| Type | Description |
|---|---|
AssertionError | if input_matrix is not a valid graph state when validate is True |
Source code in graphiq/backends/state_rep_conversion.py
density_to_stabilizer(input_matrix) ¶
Convert a density matrix to stabilizer via graph representation
This works only for graph states, not for general stabilizer states that are not graph states
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
input_matrix | numpy.ndarray | a density matrix | required |
Returns:
| Type | Description |
|---|---|
list[(float, StabilizerTableau)] | a stabilizer representation |
Source code in graphiq/backends/state_rep_conversion.py
graph_to_density(input_graph) ¶
Builds a density matrix representation from a graph (adjacency matrix or networkx.Graph)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
input_graph | networkx.Graph | numpy.ndarray | the graph from which we will build a density matrix | required |
Returns:
| Type | Description |
|---|---|
numpy.ndarray | a density matrix as a numpy array |
Raises:
| Type | Description |
|---|---|
TypeError | if input_graph is not of the type of networkx.Graph or a graphiq.backends.graph.state.Graph or an adjacency matrix given by a numpy array |
Source code in graphiq/backends/state_rep_conversion.py
graph_to_stabilizer(input_graph) ¶
Convert a graph to stabilizer represented by StabilizerTableau
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
input_graph | networkX.Graph | numpy.ndarray | the input graph to be converted to the stabilizer | required |
Returns:
| Type | Description |
|---|---|
list[(float, StabilizerTableau)] | two binary matrices representing the stabilizer generators |
Raises:
| Type | Description |
|---|---|
TypeError | if input_graph is not of the type of networkx.Graph or an adjacency matrix given by a numpy array or a list |
Source code in graphiq/backends/state_rep_conversion.py
mixed_graph_equivalency(graph1, graph2) ¶
Identify if two mixed state graph representations are the same.
Assuming no repetitive graph in each graph list. Two graphs are equal (not isomorphism).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
graph1 | list((float, networkX.Graph)) | networkX.Graph | the first graph | required |
graph2 | list((float, networkX.Graph)) | networkX.Graph | the second graph | required |
Returns:
| Type | Description |
|---|---|
bool | True if graph1 is the same as graph2; False otherwise |
Source code in graphiq/backends/state_rep_conversion.py
mixed_stabilizer_equivalency(stab1, stab2) ¶
Identify if two mixed state stabilizer representations are the same.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
stab1 | list[(float, StabilizerTableau)] | StabilizerTableau | the first stabilizer state | required |
stab2 | list[(float, StabilizerTableau)] | StabilizerTableau | the second stabilizer state | required |
Returns:
| Type | Description |
|---|---|
bool | if stab1 is the same as stab2 |
Source code in graphiq/backends/state_rep_conversion.py
stabilizer_to_density(input_stabilizer) ¶
Convert a stabilizer to a density matrix
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
input_stabilizer | list[(float, StabilizerTableau)] | StabilizerTableau | the stabilizer representation | required |
Returns:
| Type | Description |
|---|---|
numpy.ndarray | a density matrix |
Source code in graphiq/backends/state_rep_conversion.py
stabilizer_to_graph(input_stabilizer, validate=True) ¶
Convert a stabilizer to graph.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
input_stabilizer | list[(float, StabilizerTableau)] | StabilizerTableau | the stabilizer representation in terms of (probability, StabilizerTableau) | required |
validate | bool | validate if input_stabilizer is a graph state when enabled | True |
Returns:
| Type | Description |
|---|---|
[(float, numpy.ndarray)] | a graph representation |
Raises:
| Type | Description |
|---|---|
AssertionError | if input stabilizer is not a graph state when validate is True |
Source code in graphiq/backends/state_rep_conversion.py
state_to_graph(state) ¶
A helper function to turn any valid representation into a graph. It also returns the StabilizerTableau corresponding to the initial state, and the gate sequence needed to convert the state into a graph-state.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state | StabilizerTableau | CliffordTableau | networkX.Graph | np.ndarray | the state to be converted to graph | required |
Returns:
| Type | Description |
|---|---|
tuple (networkX.Graph, StabilizerTableau, list) | tuple (graph, input state's stabilizer tableau, gate_list) |
Source code in graphiq/backends/state_rep_conversion.py
1.2 Local-Clifford equivalence¶
graphiq.backends.lc_equivalence_check ¶
Local-Clifford equivalence check determines whether two graph states are local-Clifford equivalent via :py:func:is_lc_equivalent function. This section is based on articles [1] and [2]; please refer to them for more details.
Any n-partite stabilizer state is defined by a set of :math:n commuting operators called the stabilizer generators. Each member of this set is called a stabilizer, and consists of tensor product of local Pauli operators on each of the qubits. The state is the unique common eigenstate of this set with eigenvalue equal to +1. The set itself is not unique and any set of \(n\) independent members of the stabilizer group, that is generated by this generator set, is also valid. A binary representation of the stabilizers can be obtained by the following mapping:
$$ \sigma_0 = I \rightarrow 00,\ \sigma_x \rightarrow 01,\ \sigma_z \rightarrow 10,\ \sigma_y \rightarrow 11 $$ This can be generalized to \(n\)-qubit case, which gives rise to a \(2n\)-dimensional vector. For example, \(\sigma_x \otimes \sigma_y \otimes \cdots \otimes \sigma_z \rightarrow \left( 0, 1, \cdots, 1| 1, 1, \cdots, 0 \right)\). We note that this convention is different from the one used by many other authors. Nevertheless, as we treat \(X\) and \(Z\) matrices separately, this different convention does not cause any issue.
The generators set can then be represented by \(2n \times n\) matrix, which can be thought of as two \(n \times n\) square matrices on top of each other. A column in this matrix is a \(2n\)-dimensional vector representing one stabilizer operator. We name this matrix \(S\), the top matrix \(Z\) matrix, and the bottom matrix \(X\) matrix.
Under this mapping, if \(Z_{ij}\) is equal to one and \(X_{ij}\) is zero, then it means a Pauli \(Z\) operator acting on qubit \(i\) in the \(j\)-th stabilizer operator. The other way around which is \(Z_{ij} = 0\) and \(X_{ij} =1\) is a Pauli X acting on qubit \(i\). And if both elements are 0 or 1, they correspond to identity \(I\) and Pauli \(Y\) operators, respectively. For the special case of graph states, the \(X\) matrix is equal to identity \(I\) and the \(Z\) matrix is the adjacency matrix of the graph usually shown by \(\theta\).
A general \(n\)-qubit local Clifford operation in this formalism can be represented by a \(2n \times 2n\) matrix \(Q\),which is made of 4 diagonal square matrices \(A, B, C\) and \(D\), that is, $$ Q = \begin{bmatrix} A & B \ C & D \end{bmatrix}. $$
Each \(2 \times 2\) sub-matrix, consisting of \(i\)-th diagonal elements of the \(A, B, C, D\), acts locally on the \(i\)-th qubit in the system, that is,
Considering two graph states with stabilizer generators \(S = \begin{bmatrix} \theta \\ I \end{bmatrix}\) and \(S'= \begin{bmatrix} \theta' \\ I \end{bmatrix}\) the necessary and sufficient condition for LC equivalence is the existence of the Clifford operator Q such that:
where P is the \(2n \times 2n\) matrix of the form \(P =\begin{bmatrix} 0 & I \\ I & 0 \end{bmatrix}\). This gives rise to a system of \(n^2\) linear equations to find \(4n\) unknown elements of \(Q\)
The unknowns \(a_{i},\ b_{i},\ c_{i},\ d_{i}\) should also satisfy the constraints
to be a valid Clifford operation. (This ensures that the Clifford operation on a qubit is invertible.)
In order to solve for \(Q\), one should first find a basis for the solution space of the system of linear equations.
Remember that if the system of equations is overdetermined after removing linearly dependent constraints, then there exists no solution (other than :math:Q=0 which is not a valid Clifford operation) and graph states are not equivalent for sure. It is proved that a solution for a valid \(Q\) exists, if and only if the set
has a member that satisfies the constraint. Note that this is only true for the cases where \(B\) is at least of size 4. However, this is rarely the case and has not practical significance. For smaller \(B\), one should test all possible solutions (at most 16) to see if a valid \(Q\) can be found.
In order to find a basis for set B, one needs to use the \(n^2 \times 4n\) coefficient matrix of the system of linear equations, find the linearly dependent columns (this is done in the code by a row-reduction process), and choose any complete basis \(B_{dependent}\) for that subspace, and use each member of that basis to form a set of new system of linear equations.
Where the new full rank coefficient matrix \(A\) is obtained from the old coefficient matrix after row-reduction and removing the linearly dependent rows and columns, and the inhomogeneous vectors \(y\) are the members of the basis \(B_{dependent}\). Solving for vector \(x\) will result in finding one of the members \(b\) of solution basis \(B\). As evident here, the size of \(B\) is equal to the size of \(B_{dependent}\) which is equal to the number of linearly dependent columns in the initial coefficient matrix.
If \(Q\) is found, the graphs are local-Clifford equivalent. In this case, one can translate the sub-matrices \(Q^{(i)}\) to local Clifford operations (combination of \(H\) and \(P\) gates) needed (via :py:func:local_clifford_ops function) to convert one graph state to the other.
All general local-Clifford operations \(Q\) on graph states are proved to be equivalent to a series of local complementations on the graph. A list of local complementation graph operations can also be found ( :py:func:lc_graph_operations function) that transform one graph to the other one in a step by step graph transform. Please read section IV of [2]_ for details. In order to do so one starts with making a so-called :math:R matrix which is defined as
where \(C\) and \(D\) are the lower blocks of \(Q\), the Clifford operator needed to transform the initial graph to the target one, and \(\theta\) is the adjacency matrix. The transformation \(f_{i}\) on R is defined as \(f_{i}(R) = R(\Gamma_{i}R + X_{ii}\Gamma_{i} + I)\) and the transformation \(f_{jk} := f_{j}f_{k}f_{j}\). It is proved that if a sequence of transformations of \(R\) exist such that
$$ f_{j_{M}k_{M}} \cdots f_{j_{1}k_{1}} f_{i_{N}} \cdots f_{i_{1}} (R) = I $$ then the same sequence of local complementations on corresponding graph nodes \(j_{M}k_{M}\) and \(i_{N}\) transforms the graph state the same as \(Q\) does.
In order to find the sequence of "f" transformations, one checks for elements equal to 1 in the diagonal of the \(R\)-matrix, if such element is found in \(i\)-th location and the corresponding row is not already equal to the canonical unit vector \(e_i = \left\{0, 0, \cdots, 1, 0, \cdots, 0 \right\}\) the \(f_i\) transformation is applied on \(R\)-matrix and added to the sequence. This is repeated until no such row is found. Then one searches for diagonal elements equal to zero, and for each of them searches the corresponding column to find any element equal to 1, suppose that the zero diagonal is at \(j\)-th location and the element = 1 in that column is at :math:k-th row. Then a double \(f_{jk}\) transformation is applied of \(R\)-matrix and added to the sequence. This is repeated until no more eligible element can be found in the diagonal of the \(R\)-matrix.
.. [1] Maarten Van den Nest, Jeroen Dehaene, and Bart De Moor Phys. Rev. A 70, 034302 – Published 17 September 2004
.. [2] Maarten Van den Nest, Jeroen Dehaene, and Bart De Moor Phys. Rev. A 69, 022316 – Published 24 February 2004
is_lc_equivalent(adj_matrix1, adj_matrix2, mode='deterministic', seed=0) ¶
Determines whether two graph states are local-Clifford equivalent or not, given the adjacency matrices of the two. It takes two adjacency matrices as input and returns a numpy.ndarray containing \(n\) (\(2 \times 2\) arrays = clifford operations on each qubit.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
adj_matrix1 | numpy.ndarray | the adjacency matrix of the first graph. This is equal to the binary matrix for representing Pauli Z part of the symplectic binary representation of the stabilizer generators | required |
adj_matrix2 | numpy.ndarray | the adjacency matrix of the second graph | required |
mode | str | the chosen mode for finding solutions. It can be either 'deterministic' (default) or 'random'. | 'deterministic' |
seed | int | an optional input to set the random seed for the random search approach | 0 |
Returns:
| Type | Description |
|---|---|
bool, numpy.ndarray | None | If a solution is found, returns an array of single-qubit Clifford :math: |
Raises:
| Type | Description |
|---|---|
AssertionError | if the number of rows in the row reduced matrix is less than the rank of coefficient matrix or if the number of linearly dependent columns is not equal to :math: |
Source code in graphiq/backends/lc_equivalence_check.py
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 | |
iso_equal_check(graph1, graph2) ¶
Checks if the graph1 is local-Clifford equivalent to any graph that is isomorphic to graph2
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
graph1 | networkx.Graph | initial graph | required |
graph2 | networkx.Graph | target graph | required |
Returns:
| Type | Description |
|---|---|
(bool, networkx.Graph) | If equivalent (True,the graph that graph1 is equivalent to) and if not, (False, graph1 itself) |
Source code in graphiq/backends/lc_equivalence_check.py
iso_graph_finder(input_graph) ¶
Generates the list of all graphs that are isomorphic to the input graph G. Scales with n! and faces runtime or memory issues for large graphs.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
input_graph | networkx.Graph | input graph | required |
Returns:
| Type | Description |
|---|---|
list[networkx.Graph] | the list of graphs that are isomorphic to G |
Source code in graphiq/backends/lc_equivalence_check.py
lc_graph_operations(adj_matrix, solution) ¶
Finds a list of local complementations needed to be applied on each node of the given graph to transform it into the target graph given the Clifford transformation matrix, which is the output of the solver function.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
adj_matrix | numpy.ndarray | The adjacency matrix of the first graph. This is equal to the binary matrix for representing Pauli Z part of the symplectic binary representation of the stabilizer generators | required |
solution | numpy.ndarray | an array of single-qubit Clifford :math: | required |
Returns:
| Type | Description |
|---|---|
list[str] | a list of the names of the operations that need to be applied on each qubit in the correct order. |
Source code in graphiq/backends/lc_equivalence_check.py
local_clifford_ops(solution) ¶
Finds a list of operators needed to be applied on each qubit of the first graph to transform in to the second, given the Clifford transformation matrix, which is the output of the solver function.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
solution | numpy.ndarray | an array of single-qubit Clifford :math: | required |
Returns:
| Type | Description |
|---|---|
list[str] | a list of the names of the operations that need to be applied on each qubit in the correct order. |
Source code in graphiq/backends/lc_equivalence_check.py
local_comp_graph(input_graph, node_id) ¶
Applies a local complementation on the node (specified by node_id) of the input graph input_graph and returns the resulting graph.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
input_graph | networkx.Graph | input graph | required |
node_id | int | the index of the node to apply tho local complementation on | required |
Returns:
| Type | Description |
|---|---|
networkx.Graph | a new transformed graph |
Raises:
| Type | Description |
|---|---|
AssertionError | if the node index node_id is not in the graph |
Source code in graphiq/backends/lc_equivalence_check.py
1.3 Graph relabelling module¶
graphiq.utils.relabel_module ¶
Utility functionality for creating isomorphic graphs to explore alternative circuits.
automorph_check(adj1, labels_arr) ¶
Given an initial adjacency matrix (of a graph), and an array of new permutations of the nodes' labels, this function return an array of distinct adjacency matrices resulted from those permutations. Note that the order will not remain the same as the order of the input labels.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
adj1 | numpy.ndarray | initial adjacency matrix | required |
labels_arr | numpy.ndarray | an array of new labeling | required |
Returns:
| Type | Description |
|---|---|
numpy.ndarray | An array of adjacency matrices |
Source code in graphiq/utils/relabel_module.py
check_isomorphism(graph, g_list, _only_auto=False) ¶
check if the provided graph is isomorphic to any graph in the g_list
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
graph | nx.Graph | graph to check | required |
g_list | list | graph list to check against | required |
_only_auto | bool | if set to true, instead of isomorphism, only automorphism will be checked | False |
Returns:
| Type | Description |
|---|---|
bool | True of False, if an isomorphism case was detected. |
Source code in graphiq/utils/relabel_module.py
emitter_sorted(adj_arr) ¶
Takes an iterable of adjacency matrices (ideally output of the
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
adj_arr | numpy.ndarray | An array of adjacency matrices which can be the output of the | required |
Returns:
| Type | Description |
|---|---|
list | a list of tuples (adj matrices, n_emitter) sorted by number of emitters |
Source code in graphiq/utils/relabel_module.py
get_relabel_map(g1, g2) ¶
Finds the map between nodes of g1 and g2 if they are isomorphic.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
g1 | nx.Graph | np.ndarray | networkx graph or adjacency matrix | required |
g2 | nx.Graph | np.ndarray | networkx graph or adjacency matrix | required |
Returns:
| Type | Description |
|---|---|
dict | a dictionary mapping each node of g1 to a node of g2. The map may not be unique. |
Source code in graphiq/utils/relabel_module.py
iso_finder(adj_matrix, n_iso, rel_inc_thresh=0.2, allow_exhaustive=True, sort_emit=False, label_map=False, thresh=None, seed=None) ¶
The function permutes the labels of the vertices of a graph to get n_iso distinct isomorphic graphs. The original graph will also be returned as the first element of the list and counted toward the number of found cases. The maximum number of possible distinct cases may be less than n_iso. The graph G with the nodes relabeled using consecutive integers.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
adj_matrix | numpy.ndarray | initial adjacency matrix or graph | required |
n_iso | int | number of the isomorphic graphs to look for (including the original graph) | required |
rel_inc_thresh | float | a threshold value between 0 and 1. The closer to 0 the closer we get to an exhaustive search. | 0.2 |
allow_exhaustive | bool | whether exhaustive search over all possible permutations is allowed or not. The runtime may become too long if this parameter is True. | True |
sort_emit | bool | if True the outcome is sorted by the number of emitters needed to generate each graph | False |
label_map | bool | if True, the function also returns a list of dictionaries, each representing the label mapping between the original input graph and the one in the returned list. | False |
thresh | int | a threshold value that determines how many random trials is performed in search for new permutations. The default is 5 times the number of needed permutations (=n_iso). | None |
seed | int | the seed for random sampling of labels | None |
Returns:
| Type | Description |
|---|---|
numpy.ndarray | tuple (numpy.ndarray, list[dict]) | An array of adjacency matrices. The length of the array may be less than n_iso since the maximum number of isomorphic graphs for the particular input may be reached. Or the function iteration is interrupted due to threshold values. If the label_map is True, function returns a tuple, the first item being the array of adjacency matrices and the second one a list of dictionaries that are label maps between the original and relabeled graphs. |
Source code in graphiq/utils/relabel_module.py
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | |
lc_orbit_finder(graph, comp_depth=None, orbit_size_thresh=None, with_iso=False, rand=False, rep_allowed=False) ¶
Given a graph this functions tries all possible local-complementation sequences of length up to comp_depth to come up with new distinct graphs in the orbit of the input graph. The comp_depth determines the maximum depth of the orbit explored.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
graph | Graph | original graph | required |
comp_depth | int | the maximum length of the sequence of local-complementations applied on the graph; if None, continue till the required number of graphs are found, or no new graphs are found. | None |
orbit_size_thresh | int | sets a limit on the maximum number of orbit graphs to look for | None |
with_iso | bool | if true, isomorph graphs will be kept in the orbit | False |
rep_allowed | if true the orbit finder does not check for iso or auto morphism; useful for large graphs. | False |
Returns:
| Type | Description |
|---|---|
list[nx.Graph] | list of distinct graphs in the orbit of original graph |
Source code in graphiq/utils/relabel_module.py
linear_partial_orbit(graph) ¶
Takes a linear cluster state, and returns a list of distinct graphs in the orbit. Not exhaustive. The first graph in the list is the original graph state.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
graph | Graph | original graph | required |
Returns:
| Type | Description |
|---|---|
list | a list of graphs in the LC orbit of the linear cluster state |
Source code in graphiq/utils/relabel_module.py
relabel(adj_matrix, new_labels) ¶
This function relabels the vertices of a graph given the map to new labels. The initial graph's vertices is assumed to have been labeled according to its adjacency matrix that is with the nodes labeled using consecutive integers.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
adj_matrix | numpy.ndarray | The adjacency matrix of the graph | required |
new_labels | numpy.ndarray | A permutation of the initial labels that is [0, 1, ..., n-1] where n is the number of vertices. | required |
Returns:
| Type | Description |
|---|---|
numpy.ndarray | Transformed adjacency matrix according to new labels |
Source code in graphiq/utils/relabel_module.py
remove_iso(g_list) ¶
Takes an input list of graphs and removes all the isomorphic cases, returning a list of distinct graphs
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
g_list | list[nx.Graph] | list of graphs | required |
Returns:
| Type | Description |
|---|---|
list[nx.Graph] | list of distinct graphs |
Source code in graphiq/utils/relabel_module.py
rgs_orbit_finder(graph) ¶
Takes a repeater graph state, and returns the full list of distinct graphs in the orbit. The first graph in the list is the original graph state.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
graph | Graph | original graph | required |
Returns:
| Type | Description |
|---|---|
list | a full list of graphs in the LC orbit of the graph state |
Source code in graphiq/utils/relabel_module.py
1.4 Circuit comparison¶
graphiq.utils.circuit_comparison ¶
Functions to compare quantum circuits
CNOT ¶
CZ ¶
Bases: ControlledPairOperationBase
Controlled-Z gate Operation
Source code in graphiq/circuit/ops.py
CircuitStorage ¶
Class for storing circuits, check if circuit is duplicate before adding to circuit list
Source code in graphiq/utils/circuit_comparison.py
add_new_circuit(new_circuit) ¶
Function to add new circuit to circuit list, check if new circuit is redundant.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
new_circuit | CircuitDAG | new circuit to be added | required |
Returns:
| Type | Description |
|---|---|
bool | Return True if added, False otherwise. |
Source code in graphiq/utils/circuit_comparison.py
is_redundant(new_circuit) ¶
Function to check if circuit is redundant.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
new_circuit | CircuitDAG | circuit to be checked | required |
Returns:
| Type | Description |
|---|---|
bool | True if redundant, False otherwise. |
Source code in graphiq/utils/circuit_comparison.py
ClassicalCNOT ¶
ClassicalCZ ¶
ClassicalControlledPairOperationBase ¶
Bases: OperationBase
This is used as a base class for our classical controlled gates (e.g. classical CNOT, classical CPHASE). Each ClassicalControlledPairOperationBase should have control and target registers/qubits specified, and a c_register target register/cbit specified.
Source code in graphiq/circuit/ops.py
503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 | |
__init__(control, control_type, target, target_type, c_register=0, noise=nm.NoNoise()) ¶
Creates the classically controlled gate
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
control | int OR tuple (of ints, length 2) | the control register/qubit | required |
control_type | str | 'p' if photonic qubit, 'e' if emitter qubit | required |
target | int OR tuple (of ints, length 2) | target register/qubit for the Operation | required |
target_type | 'p' if photonic qubit, 'e' if emitter qubit | required | |
c_register | int OR tuple (of ints, length 2) | the classical register/cbit | 0 |
noise | graphiq.noise.noise_models.NoiseBase | Noise model | NoNoise() |
Returns:
| Type | Description |
|---|---|
None | nothing |
Source code in graphiq/circuit/ops.py
c_registers(c_reg) ¶
Handle to modify the register-cbit pair on which the operation acts. This also automatically updates the self.c_register field
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
c_reg | the new c_register value to set | required |
Returns:
| Type | Description |
|---|---|
None | function returns nothing |
Raises:
| Type | Description |
|---|---|
ValueError | if the new c_reg object does not have a length of 1 |
Source code in graphiq/circuit/ops.py
q_registers(q_reg) ¶
Handle to modify the register-qubit pairs on which the operation acts. This also automatically updates the self.control, self.target fields
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
q_reg | the new q_register value to set | required |
Returns:
| Type | Description |
|---|---|
None | function returns nothing |
Raises:
| Type | Description |
|---|---|
ValueError | if the new q_reg object does not have a length of 2 |
Source code in graphiq/circuit/ops.py
ControlledPairOperationBase ¶
Bases: OperationBase
This is used as a base class for our quantum controlled gates (e.g. CNOT, CPHASE). Each ControlledPairOperationBase should have control and target registers/qubits specified
Source code in graphiq/circuit/ops.py
__init__(control, control_type, target, target_type, noise=nm.NoNoise()) ¶
Creates a control gate object
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
control | int OR tuple (of ints, length 2) | control register/qubit for the Operation | required |
control_type | str | 'p' if photonic qubit, 'e' if emitter qubit | required |
target | int OR tuple (of ints, length 2) | target register/qubit for the Operation | required |
target_type | str | 'p' if photonic qubit, 'e' if emitter qubit | required |
Returns:
| Type | Description |
|---|---|
None | function returns nothing |
Source code in graphiq/circuit/ops.py
q_registers(q_reg) ¶
Handle to modify the register-qubit pairs on which the operation acts. This also automatically updates the self.control, self.target fields
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
q_reg | the new q_register value to set | required |
Returns:
| Type | Description |
|---|---|
None | function returns nothing |
Raises:
| Type | Description |
|---|---|
ValueError | if the new q_reg object does not have a length of 2 |
Source code in graphiq/circuit/ops.py
Hadamard ¶
Identity ¶
Input ¶
Bases: InputOutputOperationBase
Input Operation. Serves as a placeholder in the circuit so that we know that this is where a given qubit/cbit is introduced (i.e. there are no prior operations on it)
Source code in graphiq/circuit/ops.py
InputOutputOperationBase ¶
Bases: OperationBase
This is used as a base class for our Input and Output Operations. These operations largely act as "dummy Operations" signalling the input / output of a state (useful for circuit DAG representation, for example)
Source code in graphiq/circuit/ops.py
__init__(register, reg_type, noise=nm.NoNoise()) ¶
Creates an IO base class Operation
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
register | int OR tuple (of ints, length 2) | the register/qubit which this I/O operation acts on | required |
reg_type | str | the input/output is for a quantum photonic register if 'p', for a quantum emitter register if 'e', and for a classical register if 'c' | required |
noise | graphiq.noise.noise_models.NoiseBase | Noise model | NoNoise() |
Returns:
| Type | Description |
|---|---|
None | nothing |
Source code in graphiq/circuit/ops.py
c_registers(c_reg) ¶
Handle to modify the register-cbit pairs on which the operation acts. This also automatically updates the self.register field, if the I/O is classical
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
c_reg | the new c_register value to set (if any) | required |
Returns:
| Type | Description |
|---|---|
None | function returns nothing |
Raises:
| Type | Description |
|---|---|
ValueError | if the new c_reg object does not match the length of self.c_registers (Operations should not have variable register numbers) |
Source code in graphiq/circuit/ops.py
q_registers(q_reg) ¶
Handle to modify the register-qubit pairs on which the operation acts. This also automatically updates the self.register field, if the I/O is quantum
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
q_reg | the new q_register value to set (if any) | required |
Returns:
| Type | Description |
|---|---|
None | function returns nothing |
Raises:
| Type | Description |
|---|---|
ValueError | if the new q_reg object does not match the length of self.q_registers (Operations should not have variable register numbers) |
Source code in graphiq/circuit/ops.py
MeasurementCNOTandReset ¶
Bases: ClassicalControlledPairOperationBase
Measurement-controlled X gate Operation with resetting the control qubit after measurement
Source code in graphiq/circuit/ops.py
MeasurementZ ¶
Bases: OperationBase
Z Measurement Operation
Source code in graphiq/circuit/ops.py
__init__(register=0, reg_type='e', c_register=0, noise=nm.NoNoise()) ¶
Creates a Z measurement Operation
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
register | int OR tuple (of ints, length 2) | the quantum register on which the measurement is performed | 0 |
reg_type | str | 'p' if photonic qubit, 'e' if emitter qubit | 'e' |
c_register | int OR tuple (of ints, length 2) | the classical register to which the measurement result is saved | 0 |
noise | src.noise.noise_models.NoiseBase | Noise model | NoNoise() |
Returns:
| Type | Description |
|---|---|
None | this function returns nothing |
Source code in graphiq/circuit/ops.py
c_registers(c_reg) ¶
Handle to modify the register-cbit pair to which measurements are saved. This also automatically updates the self.c_register field
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
c_reg | the new c_register value to set | required |
Returns:
| Type | Description |
|---|---|
None | function returns nothing |
Raises:
| Type | Description |
|---|---|
ValueError | if the new c_reg object does not have a length of 1 |
Source code in graphiq/circuit/ops.py
q_registers(q_reg) ¶
Handle to modify the register-qubit pair which is measured. This also automatically updates the self.register field
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
q_reg | the new q_register value to set | required |
Returns:
| Type | Description |
|---|---|
None | function returns nothing |
Raises:
| Type | Description |
|---|---|
ValueError | if the new q_reg object does not have a length of 1 |
Source code in graphiq/circuit/ops.py
OneQubitGateWrapper ¶
Bases: OneQubitOperationBase
This wrapper class allows us to compose a list of one-qubit operation and treat them as a single component within the circuit (this allows us, for example, to create every local Clifford gate with other gates, without having to separately implement every combination within the compiler)
Source code in graphiq/circuit/ops.py
unwrap() ¶
Unwraps the Operation into a list of sub-operations (this can be useful for any operations which are composed of multiple other operations) in the reverse order, which corresponds to the order of applying gates
Returns:
| Type | Description |
|---|---|
list | a sequence of base operations (i.e. operations which are not compositions of other operations) |
Source code in graphiq/circuit/ops.py
OneQubitOperationBase ¶
Bases: OperationBase
This is used as a base class for any one-qubit operation (one-qubit operations should all depend on a single parameter, "register"
Source code in graphiq/circuit/ops.py
__init__(register, reg_type, noise=nm.NoNoise()) ¶
Creates a one-qubit operation base class object
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
register | int OR tuple (of ints, length 2) | the (quantum) register on which the single-qubit operation acts | required |
reg_type | str | 'e' if emitter qubit, 'p' if a photonic qubit | required |
noise | graphiq.noise.noise_models.NoiseBase | Noise model | NoNoise() |
Returns:
| Type | Description |
|---|---|
None | nothing |
Source code in graphiq/circuit/ops.py
q_registers(q_reg) ¶
Handle to modify the register-qubit pairs on which the operation acts. This also automatically updates the self.register field
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
q_reg | the new q_register value to set | required |
Returns:
| Type | Description |
|---|---|
None | nothing |
Raises:
| Type | Description |
|---|---|
ValueError | if the new q_reg object does not have a length of 1 |
Source code in graphiq/circuit/ops.py
OperationBase ¶
Bases: ABC
Base class from which operations will inherit
Source code in graphiq/circuit/ops.py
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 | |
c_registers property writable ¶
Returns the c_registers tuple of the Operation class
Returns:
| Type | Description |
|---|---|
tuple | the c_registers_tuple |
labels property writable ¶
Returns the list of labels of this operation
Returns:
| Type | Description |
|---|---|
list[str] | list of labels |
q_registers property writable ¶
Returns the quantum registers tuple of the Operation class
Returns:
| Type | Description |
|---|---|
tuple | the registers tuple |
q_registers_type property writable ¶
Returns the quantum registers types ('e' for emitter, 'p' for photons) tuple of the Operation class
Returns:
| Type | Description |
|---|---|
tuple | the registers type |
__init__(q_registers=tuple(), q_registers_type=tuple(), c_registers=tuple(), noise=nm.NoNoise(), params=tuple(), param_info=dict()) ¶
Creates an Operation base object (which is largely responsible for holding the registers on which an operation act--this provides a consistent API for the circuit class to use when dealing with any arbitrary operation).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
q_registers | tuple (tuple of: integers OR tuples of length 2) | (a, ..., b) indices (indicating registers a, ..., b) OR ((a, b), ...) indicating photonic qubit b of register a OR any combination of (reg, bit) notation and reg notation These tuples can be of length 1 or empty as well, depending on the number of registers the gate requires | tuple() |
q_registers_type | tuple of strings, each is either 'p' (photonic qubit) or 'e' (emitter qubit) | tuple() | |
c_registers | tuple (tuple of: integers OR tuples of length 2) | same as photon/emitter_registers, but for the classical registers | tuple() |
noise | graphiq.noise.noise_models.NoiseBase | Noise model | NoNoise() |
params | tuple | an ordered list of parameter values for a parameterized gate | tuple() |
param_info | dict | a dictionary that specifies all the information regarding parameters | dict() |
Returns:
| Type | Description |
|---|---|
None | nothing |
Raises:
| Type | Description |
|---|---|
AssertionError | if photon_register, emitter_register, c_registers are not tuples, OR if the elements of the tuple do not correspond to the notation described above |
Source code in graphiq/circuit/ops.py
add_labels(new_labels) ¶
Adds new labels to existing labels
Returns:
| Type | Description |
|---|---|
None | nothing |
openqasm_info() classmethod ¶
Returns the information needed to generate an openQASM script using this operation (needed imports, how to define a gate, how to use a gate), packaged as a single object. This is used by the Circuit classes to generate openQASM scripts when needed
Returns:
| Type | Description |
|---|---|
| the operation class's openqasm information (possibly None) |
Source code in graphiq/circuit/ops.py
parse_q_reg_types() ¶
Find a proper string description of the register types relevant for this operation
Returns:
| Type | Description |
|---|---|
str | a string description |
Raises:
| Type | Description |
|---|---|
ValueError | if the quantum register type is not supported |
Source code in graphiq/circuit/ops.py
unwrap() ¶
Unwraps the Operation into a list of sub-operations (this can be useful for any operations which are composed of multiple other operations) in the reverse order, which corresponds to the order of applying operations.
Returns:
| Type | Description |
|---|---|
list | a sequence of base operations (i.e. operations which are not compositions of other operations) |
Source code in graphiq/circuit/ops.py
Output ¶
Bases: InputOutputOperationBase
Input Operation. Serves as a placeholder in the circuit so that we know that this is the final operation on a qubit/cbit (i.e. there are no subsequent operations on it)
Source code in graphiq/circuit/ops.py
ParameterizedControlledRotationQubit ¶
Bases: ControlledPairOperationBase
Parameterized two qubit controlled gate,
Source code in graphiq/circuit/ops.py
ParameterizedOneQubitRotation ¶
Bases: OneQubitOperationBase
Parameterized one qubit rotation.
Source code in graphiq/circuit/ops.py
Phase ¶
Bases: OneQubitOperationBase
Phase gate operation, P = diag(1, i)
Source code in graphiq/circuit/ops.py
PhaseDagger ¶
Bases: OneQubitOperationBase
Phase gate operation, P_dag = diag(1, -i)
Source code in graphiq/circuit/ops.py
RX ¶
Bases: ParameterizedOneQubitRotation
Rotation around the X axis
Source code in graphiq/circuit/ops.py
RY ¶
Bases: ParameterizedOneQubitRotation
Rotation around the Y axis
Source code in graphiq/circuit/ops.py
RZ ¶
Bases: ParameterizedOneQubitRotation
Rotation around the Z axis
Source code in graphiq/circuit/ops.py
SigmaX ¶
SigmaY ¶
SigmaZ ¶
add_control_target_to_dag(circuit) ¶
Process the input circuit DAG and add control_target attribute to edges.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
circuit | CircuitDAG | circuit | required |
Returns:
| Type | Description |
|---|---|
None | nothing |
Source code in graphiq/utils/circuit_comparison.py
circuit_is_isomorphic(circuit1, circuit2) ¶
Compare 2 circuits using nx.is_isomorphic from their DAG.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
circuit1 | CircuitDAG | circuit 1 | required |
circuit2 | CircuitDAG | circuit 2 | required |
Returns:
| Type | Description |
|---|---|
Boolean | True if 2 circuits is isomorphic, False otherwise. |
Source code in graphiq/utils/circuit_comparison.py
class_to_name_mapping(class_op) ¶
Function to map operation class to string. It's used to convert circuit object to json.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
class_op | operation | operation class | required |
Returns:
| Type | Description |
|---|---|
| |
Source code in graphiq/circuit/ops.py
compare_circuits(circuit1, circuit2, method='direct') ¶
Compare two circuits by using GED or direct loop method
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
circuit1 | CircuitDAG | circuit that to be compared | required |
circuit2 | CircuitDAG | circuit that to be compared | required |
method | str | Determine which comparison function to use | 'direct' |
Returns:
| Type | Description |
|---|---|
bool | whether two circuits are the same |
Source code in graphiq/utils/circuit_comparison.py
direct(circuit1, circuit2) ¶
Directly compare two circuits by iterating from input nodes to output nodes
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
circuit1 | CircuitDAG | circuit that to be compared | required |
circuit2 | CircuitDAG | circuit that to be compared | required |
Returns:
| Type | Description |
|---|---|
bool | whether two circuits are the same |
Source code in graphiq/utils/circuit_comparison.py
find_local_clifford_by_matrix(matrix) ¶
Find local Clifford by its matrix representation
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
matrix | numpy.ndarray | a matrix representation of one-qubit Clifford gate | required |
Returns:
| Type | Description |
|---|---|
list | the local Clifford gate specified by a list of basic gates it consists of or None if the input matrix is not a valid one-qubit Clifford gate |
Raises:
| Type | Description |
|---|---|
ValueError | if the matrix does not correspond to a valid one-qubit Clifford gate. |
Source code in graphiq/circuit/ops.py
ged(circuit1, circuit2, full=True) ¶
Calculate Graph Edit Distance (GED) between two circuits. Further reading on GED: https://networkx.org/documentation/stable/reference/algorithms/similarity.html
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
circuit1 | CircuitDAG | circuit that to be compared | required |
circuit2 | CircuitDAG | circuit that to be compared | required |
full | bool | Determine which GED function to use | True |
Returns:
| Type | Description |
|---|---|
bool | whether two circuits are the same |
Source code in graphiq/utils/circuit_comparison.py
ged_adaptive(circuit1, circuit2, threshold=30) ¶
Switch between exact and approximate GED calculation adaptively
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
circuit1 | CircuitDAG | circuit that to be compared | required |
circuit2 | CircuitDAG | circuit that to be compared | required |
threshold | int | threshold | 30 |
Returns:
| Type | Description |
|---|---|
bool | exact/approximated GED between circuits(cost needed to transform self.dag to circuit_compare.dag) |
Source code in graphiq/utils/circuit_comparison.py
local_clifford_to_matrix_map(gate) ¶
Find the 2 X 2 matrix corresponding to the local Clifford gate
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
gate | list | a subclass of OperationBase | the local Clifford gate | required |
Returns:
| Type | Description |
|---|---|
numpy.ndarray | the 2 X 2 matrix corresponding to the local Clifford gate |
Source code in graphiq/circuit/ops.py
local_cliffords_name_to_matrix_map() ¶
Find all one-qubit local Clifford gates in the matrix representation
Returns:
| Type | Description |
|---|---|
map | all one-qubit local Clifford gates in the matrix representation |
Source code in graphiq/circuit/ops.py
name_to_class_map(name) ¶
Maps our openqasm naming scheme to operation classes. Does not handle multi-gate wrappers Does not handle multi-line openqasm components
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name | str | gate name in openqasm | required |
Returns:
| Type | Description |
|---|---|
OperationBase class | the operation class corresponding to the openqasm name if the name is a valid name |
Source code in graphiq/circuit/ops.py
one_qubit_cliffords() ¶
Returns an iterator of single-qubit clifford gates
Returns:
| Type | Description |
|---|---|
map | iterator covering each single-qubit clifford gate |
Source code in graphiq/circuit/ops.py
remove_redundant_circuits(circuit_list) ¶
The function will remove redundant circuit in a list of circuits by running the circuit_is_isomorphic() function. The function returns a new list of circuits with redundant circuits are removed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
circuit_list | list | a list of circuits | required |
Returns:
| Type | Description |
|---|---|
list | a new list of circuits |
Source code in graphiq/utils/circuit_comparison.py
simplify_local_clifford(gate_list) ¶
Simplify the list of basic gates that represents a local Clifford
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
gate_list | list | original list of basic gates that represents a local Clifford | required |
Returns:
| Type | Description |
|---|---|
list | simplified list of basic gates that represents the same local Clifford |