.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "gallery/tn_simulation.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note Click :ref:`here ` to download the full example code .. rst-class:: sphx-glr-example-title .. _sphx_glr_gallery_tn_simulation.py: Usage and application of tensor network simulations ===================================================== In this example, we will illustrate the usage and application of TN simulation of MBQC. Firstly, let's import the relevant modules: .. GENERATED FROM PYTHON SOURCE LINES 11-26 .. code-block:: default from functools import reduce import cotengra as ctg import matplotlib.pyplot as plt import networkx as nx import numpy as np import opt_einsum as oe import quimb.tensor as qtn from scipy.optimize import minimize from graphix import Circuit rng = np.random.default_rng() .. GENERATED FROM PYTHON SOURCE LINES 27-30 This application will be for the QAOA (Quantum Approximate Optimization Algorithm), which is an algorithm that can be used for example to solve combinatorical optimization problems. For this example a max cut problem will be solved on a star-like graph, so we can easier validate the results. .. GENERATED FROM PYTHON SOURCE LINES 32-33 Let's start with defining a helper function for buidling the circuit. .. GENERATED FROM PYTHON SOURCE LINES 33-45 .. code-block:: default def ansatz(circuit, n, gamma, beta, iterations): for j in range(iterations): for i in range(1, n): circuit.cnot(i, 0) circuit.rz(0, gamma[j]) circuit.cnot(i, 0) for i in range(n): circuit.rx(i, beta[j]) .. GENERATED FROM PYTHON SOURCE LINES 46-47 Let's look at how the quantum circuit is going to be built. .. GENERATED FROM PYTHON SOURCE LINES 47-62 .. code-block:: default n = 5 # This will result in a graph that would be too large for statevector simulation. iterations = 2 # Define the number of iterations in the quantum circuit. gamma = 2 * np.pi * rng.random(iterations) beta = 2 * np.pi * rng.random(iterations) # Define the circuit. circuit = Circuit(n) ansatz(circuit, n, gamma, beta, iterations) # Transpile Circuit into pattern as it is needed for creating the TN. pattern = circuit.transpile().pattern # Optimizing according to standardization algorithm of graphix. pattern.standardize() pattern.shift_signals() .. rst-class:: sphx-glr-script-out .. code-block:: none {6: {0}, 7: {5}, 8: {0, 6}, 9: {5, 7}, 10: {8, 0, 6}, 11: {9, 5, 7}, 12: {8, 0, 10, 6}, 13: {9, 11, 5, 7}, 14: {0, 6, 8, 10, 12}, 15: {5, 7, 9, 11, 13}, 16: {0, 6, 8, 10, 12, 14}, 17: {5, 7, 9, 11, 13, 15}, 18: {0, 6, 8, 10, 12, 14, 16}, 19: {5, 7, 9, 11, 13, 15, 17}, 20: {0, 6, 8, 10, 12, 14, 16, 18}, 21: {5, 7, 9, 11, 13, 15, 17, 19}, 22: {0, 6, 8, 10, 12, 14, 16, 18, 20}, 23: {5, 7, 9, 11, 13, 15, 17, 19, 21}, 24: {0, 6, 8, 10, 12, 14, 16, 18, 20, 22}, 25: {5, 7, 9, 11, 13, 15, 17, 19, 21, 23}, 26: {0, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24}, 27: {5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25}, 28: {0, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26}, 29: {5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27}, 1: {8, 6}, 2: {12, 14}, 3: {18, 20}, 4: {24, 26}, 30: {0, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28}, 39: {5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31}, 40: {0, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30}, 41: {5, 7, 39, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31}, 42: {0, 6, 40, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30}, 43: {5, 39, 7, 41, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29}, 44: {0, 6, 8, 40, 42, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30}, 45: {33, 5, 7, 39, 41, 9, 43, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29}, 46: {0, 6, 8, 40, 42, 10, 44, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30}, 47: {33, 5, 39, 7, 41, 9, 43, 11, 45, 13, 15, 17, 19, 21, 23, 25, 27, 29}, 48: {0, 6, 8, 40, 42, 10, 44, 12, 46, 14, 16, 18, 20, 22, 24, 26, 28, 30}, 49: {5, 39, 7, 41, 9, 43, 11, 45, 13, 47, 15, 17, 19, 21, 23, 25, 27, 29}, 50: {0, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 40, 42, 44, 46, 48}, 51: {5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 35, 39, 41, 43, 45, 47, 49}, 52: {0, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 40, 42, 44, 46, 48, 50}, 53: {5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 35, 39, 41, 43, 45, 47, 49, 51}, 54: {0, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 40, 42, 44, 46, 48, 50, 52}, 55: {5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 39, 41, 43, 45, 47, 49, 51, 53}, 56: {0, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 40, 42, 44, 46, 48, 50, 52, 54}, 57: {5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 37, 39, 41, 43, 45, 47, 49, 51, 53, 55}, 58: {0, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 40, 42, 44, 46, 48, 50, 52, 54, 56}, 59: {5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 37, 39, 41, 43, 45, 47, 49, 51, 53, 55, 57}, 60: {0, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58}, 61: {5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 39, 41, 43, 45, 47, 49, 51, 53, 55, 57, 59}, 62: {0, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60}, 63: {5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 39, 41, 43, 45, 47, 49, 51, 53, 55, 57, 59, 61}, 32: {1, 6, 8, 40, 42}, 65: {31}, 34: {2, 12, 14, 46, 48}, 67: {33}, 36: {3, 18, 20, 52, 54}, 69: {35}, 38: {4, 24, 26, 58, 60}, 71: {37}} .. GENERATED FROM PYTHON SOURCE LINES 63-64 Print some properties of the graph. .. GENERATED FROM PYTHON SOURCE LINES 64-69 .. code-block:: default nodes, edges = pattern.get_graph() print(f"Number of nodes: {len(nodes)}") print(f"Number of edges: {len(edges)}") .. rst-class:: sphx-glr-script-out .. code-block:: none Number of nodes: 73 Number of edges: 84 .. GENERATED FROM PYTHON SOURCE LINES 70-71 Optimizing by performing Pauli measurements in the pattern using efficient stabilizer simulator. .. GENERATED FROM PYTHON SOURCE LINES 71-74 .. code-block:: default pattern.perform_pauli_measurements(use_rustworkx=True) .. rst-class:: sphx-glr-script-out .. code-block:: none /home/docs/checkouts/readthedocs.org/user_builds/graphix/checkouts/v0.3.0/graphix/graphsim/graphstate.py:26: UserWarning: rustworkx is not installed. Using networkx instead. warnings.warn("rustworkx is not installed. Using networkx instead.", stacklevel=1) .. GENERATED FROM PYTHON SOURCE LINES 75-78 Simulate using the TN backend of graphix, which will return an MBQCTensorNet object. The graph_prep argument is optional, but with 'parallel' the TensorNetworkBackend will prepeare the graph state faster. .. GENERATED FROM PYTHON SOURCE LINES 78-83 .. code-block:: default mbqc_tn = pattern.simulate_pattern(backend="tensornetwork", graph_prep="parallel") sv = mbqc_tn.to_statevector().flatten() print("Statevector after the simulation:", sv) .. rst-class:: sphx-glr-script-out .. code-block:: none Statevector after the simulation: [ 0.03672859-0.14810786j -0.12650245-0.04949416j -0.12650245-0.04949416j -0.11637256+0.07856757j -0.12650245-0.04949416j -0.11637256+0.07856757j -0.11637256+0.07856757j -0.13013507+0.1224057j -0.12650245-0.04949416j -0.11637256+0.07856757j -0.11637256+0.07856757j -0.13013507+0.1224057j -0.11637256+0.07856757j -0.13013507+0.1224057j -0.13013507+0.1224057j -0.32061384+0.23269269j -0.32061384+0.23269269j -0.13013507+0.1224057j -0.13013507+0.1224057j -0.11637256+0.07856757j -0.13013507+0.1224057j -0.11637256+0.07856757j -0.11637256+0.07856757j -0.12650245-0.04949416j -0.13013507+0.1224057j -0.11637256+0.07856757j -0.11637256+0.07856757j -0.12650245-0.04949416j -0.11637256+0.07856757j -0.12650245-0.04949416j -0.12650245-0.04949416j 0.03672859-0.14810786j] .. GENERATED FROM PYTHON SOURCE LINES 84-87 Let's explore what is really happening, how the TN is being constructed. The tensor network is created using the graph structure, so from the list of nodes as well as the edges. The graph must be preprocessed before the constraction of the TN itself. .. GENERATED FROM PYTHON SOURCE LINES 89-94 The goal is to represent quantum states, so for every node a list is created, which stores the index labels for each dimension for that given node. Its length will be one larger than the number of edges of the node. This is due to the fact that the first entry of the list represents the "dangling" index, which corresponds to the physical index of the qubit (i.e., the index that represents the local Hilbert space of the qubit). The following entries in the list are then correspond to neighbouring tensors, and can be contracted with them. For additional details and visualization visit: https://quimb.readthedocs.io/en/latest/tensor-1d.html. .. GENERATED FROM PYTHON SOURCE LINES 96-98 Let's take a closer look at an MPS tensor (left plot) and an MPS tensor network that consists of two MPS tensors (right plot). By the network on the right the middle index is shared between the two tensors, essentially allowing for contraction between them by summing over it. .. GENERATED FROM PYTHON SOURCE LINES 98-114 .. code-block:: default t = qtn.rand_tensor([2], "a") fig, ax = plt.subplots(1, 2) t.draw( ax=ax[0], title="MPS tensor", legend=False, ) t1 = qtn.rand_tensor([2, 2], ["a", "b"]) t2 = qtn.rand_tensor([1, 2], ["b", "c"]) t1.add_tag("T1") t2.add_tag("T2") t = qtn.TensorNetwork([t1, t2]) t.draw(ax=ax[1], title="MPS", legend=False, color=["T1", "T2"]) plt.show() .. image-sg:: /gallery/images/sphx_glr_tn_simulation_001.png :alt: MPS tensor, MPS :srcset: /gallery/images/sphx_glr_tn_simulation_001.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 115-122 Additionally, the type of edges are also stored, in a binary valued list for each node. These are used to construct the tensor itself. From each node in the graph a tensor is constructed, which has a dimension that is exactly one larger than its neighbour count. The tensor is described using two outer products, for which the list from above is used, that describes the edges for every node. For additional information on TN construction please refer to: https://journals.aps.org/pra/abstract/10.1103/PhysRevA.76.052315 . Section III A provides further information on Matrix Porduct States and section III C gives an example using a 1-D cluster state. In section IV novel resource states are explored, where parts A, B can be used for getting a deeper understanding. .. GENERATED FROM PYTHON SOURCE LINES 124-126 Let's also plot the resulting tensor network (notice that there are five dangling edges, which is exactly the number of qubits that were defined in the quantum circuit). Here open means that the node has a dangling index, but the "dangling" edge itself is not drawn, rather the tensors are color coded. .. GENERATED FROM PYTHON SOURCE LINES 126-140 .. code-block:: default fig, ax = plt.subplots(figsize=(13, 10)) color = ["Open", "Close"] mbqc_tn.draw( ax=ax, color=color, show_tags=False, show_inds=False, iterations=100, k=0.01, ) plt.show() .. image-sg:: /gallery/images/sphx_glr_tn_simulation_002.png :alt: tn simulation :srcset: /gallery/images/sphx_glr_tn_simulation_002.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 141-142 Let's calculate the measuring probability corresponding to the first basis state. .. GENERATED FROM PYTHON SOURCE LINES 142-146 .. code-block:: default value = mbqc_tn.get_basis_amplitude(0) print(f"Probability for {0} is {value}") .. rst-class:: sphx-glr-script-out .. code-block:: none Probability for 0 is 0.0232849269270546 .. GENERATED FROM PYTHON SOURCE LINES 147-149 It is also possible to change the path contraction algorithm. Let's explore that too and define a custom optimizer for contraction, that we can use later. .. GENERATED FROM PYTHON SOURCE LINES 149-156 .. code-block:: default opt = ctg.HyperOptimizer( minimize="combo", reconf_opts={}, progbar=True, ) .. GENERATED FROM PYTHON SOURCE LINES 157-159 Let's also calculate the expectation value for the measurement in the computational basis. The expectation value can be optiained using a function of graphix. .. GENERATED FROM PYTHON SOURCE LINES 159-167 .. code-block:: default pauli_z = np.array([[1, 0], [0, -1]]) identity = np.array([[1, 0], [0, 1]]) operator = reduce(np.kron, [pauli_z] * n) # Use the defined optimizer by setting the 'optimize' parameter. exp_val = mbqc_tn.expectation_value(operator, range(n), optimize=opt) print("Expectation value for Z^n: ", exp_val) .. rst-class:: sphx-glr-script-out .. code-block:: none 0%| | 0/128 [00:00`` and ``|00001>`` because of bit ordering), which mean that splitting the graph so that node number 0 is in one set, and all other nodes in the other solves the max cut problem. This result is what we would expect from this star-like graph. .. GENERATED FROM PYTHON SOURCE LINES 253-255 The following illustration shows the starting graph on the left, and the graph with the resulting sets found on the right, where the nodes with different colours belong to different groups. .. GENERATED FROM PYTHON SOURCE LINES 255-266 .. code-block:: default fig, ax = plt.subplots(ncols=2, figsize=(8, 6)) ax = ax.flatten() g = nx.Graph() for i in range(1, n): g.add_edge(0, i) color = ["blue"] * n color[0] = "red" nx.draw(g, ax=ax[0], with_labels=True) nx.draw(g, ax=ax[1], node_color=color, with_labels=True) plt.show() .. image-sg:: /gallery/images/sphx_glr_tn_simulation_004.png :alt: tn simulation :srcset: /gallery/images/sphx_glr_tn_simulation_004.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-timing **Total running time of the script:** ( 0 minutes 46.343 seconds) .. _sphx_glr_download_gallery_tn_simulation.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: tn_simulation.py ` .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: tn_simulation.ipynb ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_