.. 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 :ref:`Go to the end ` 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-36 .. code-block:: Python from __future__ import annotations from functools import reduce from typing import TYPE_CHECKING import cotengra as ctg import matplotlib.pyplot as plt import networkx as nx import numpy as np import quimb.tensor as qtn from cotengra.oe import PathOptimizer from scipy.optimize import minimize from graphix import Circuit from graphix.fundamentals import ANGLE_PI if TYPE_CHECKING: from collections.abc import Collection, Sequence import numpy.typing as npt from graphix.fundamentals import Angle rng = np.random.default_rng() .. GENERATED FROM PYTHON SOURCE LINES 37-40 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 42-43 Let's start with defining a helper function for buidling the circuit. .. GENERATED FROM PYTHON SOURCE LINES 43-61 .. code-block:: Python def ansatz( circuit: Circuit, n: int, gamma: npt.NDArray[np.float64] | Sequence[Angle], beta: npt.NDArray[np.float64] | Sequence[Angle], iterations: int, ) -> None: 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 62-63 Let's look at how the quantum circuit is going to be built. .. GENERATED FROM PYTHON SOURCE LINES 63-78 .. code-block:: Python 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 * ANGLE_PI * rng.random(iterations) beta = 2 * ANGLE_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 79-80 Print some properties of the graph. .. GENERATED FROM PYTHON SOURCE LINES 80-85 .. code-block:: Python graph = pattern.extract_graph() print(f"Number of nodes: {len(graph.nodes)}") print(f"Number of edges: {len(graph.edges)}") .. rst-class:: sphx-glr-script-out .. code-block:: none Number of nodes: 73 Number of edges: 84 .. GENERATED FROM PYTHON SOURCE LINES 86-87 Optimizing by performing Pauli measurements in the pattern using efficient stabilizer simulator. .. GENERATED FROM PYTHON SOURCE LINES 87-90 .. code-block:: Python pattern.remove_input_nodes() pattern.perform_pauli_measurements() .. GENERATED FROM PYTHON SOURCE LINES 91-94 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 94-99 .. code-block:: Python 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 /home/docs/checkouts/readthedocs.org/user_builds/graphix/checkouts/v0.3.5/examples/tn_simulation.py:95: UserWarning: Default random-number generator is used. Results may not be reproducible. mbqc_tn = pattern.simulate_pattern(backend="tensornetwork", graph_prep="parallel") Statevector after the simulation: [ 0.17184155-0.32619912j 0.02261169-0.12900802j 0.02261169-0.12900802j -0.09077854-0.02047136j 0.02261169-0.12900802j -0.09077854-0.02047136j -0.09077854-0.02047136j -0.13134048-0.0553517j 0.02261169-0.12900802j -0.09077854-0.02047136j -0.09077854-0.02047136j -0.13134048-0.0553517j -0.09077854-0.02047136j -0.13134048-0.0553517j -0.13134048-0.0553517j -0.36367789-0.17311884j -0.36367789-0.17311884j -0.13134048-0.0553517j -0.13134048-0.0553517j -0.09077854-0.02047136j -0.13134048-0.0553517j -0.09077854-0.02047136j -0.09077854-0.02047136j 0.02261169-0.12900802j -0.13134048-0.0553517j -0.09077854-0.02047136j -0.09077854-0.02047136j 0.02261169-0.12900802j -0.09077854-0.02047136j 0.02261169-0.12900802j 0.02261169-0.12900802j 0.17184155-0.32619912j] .. GENERATED FROM PYTHON SOURCE LINES 100-103 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 105-110 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 112-114 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 114-130 .. code-block:: Python 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") tn = qtn.TensorNetwork([t1, t2]) tn.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 131-138 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 140-142 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 142-156 .. code-block:: Python 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 157-158 Let's calculate the measuring probability corresponding to the first basis state. .. GENERATED FROM PYTHON SOURCE LINES 158-162 .. code-block:: Python value = mbqc_tn.basis_amplitude(0) print(f"Probability for {0} is {value}") .. rst-class:: sphx-glr-script-out .. code-block:: none Probability for 0 is 0.13593538772526156 .. GENERATED FROM PYTHON SOURCE LINES 163-165 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 165-172 .. code-block:: Python opt: PathOptimizer = ctg.HyperOptimizer( minimize="combo", reconf_opts={}, progbar=True, ) .. rst-class:: sphx-glr-script-out .. code-block:: none /home/docs/checkouts/readthedocs.org/user_builds/graphix/envs/v0.3.5/lib/python3.11/site-packages/cotengra/hyperoptimizers/hyper.py:36: UserWarning: Couldn't import `kahypar` - skipping from default hyper optimizer and using basic `labels` method instead. `kahypar` is highly recommended for the best quality contraction paths. warnings.warn( /home/docs/checkouts/readthedocs.org/user_builds/graphix/envs/v0.3.5/lib/python3.11/site-packages/cotengra/hyperoptimizers/hyper.py:75: UserWarning: Couldn't find `optuna`, `cmaes`, or `nevergrad` so will use completely random sampling in place of hyper-optimization. It is recommended to install one of these libraries for higher quality hyper-optimization. warnings.warn( .. GENERATED FROM PYTHON SOURCE LINES 173-175 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 175-183 .. code-block:: Python 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 float: circuit = Circuit(n) gamma = params[:slice_index] beta = params[slice_index:] ansatz(circuit, n, gamma, beta, quantum_iter) pattern = circuit.transpile().pattern pattern.standardize() pattern.shift_signals() pattern.remove_input_nodes() pattern.perform_pauli_measurements() mbqc_tn = pattern.simulate_pattern(backend="tensornetwork", graph_prep="parallel") exp_val: float = 0 for op in ham: exp_val += np.real(mbqc_tn.expectation_value(op, range(n), optimize=opt)) return exp_val .. GENERATED FROM PYTHON SOURCE LINES 215-216 We want to find the ground state energy for the Hamiltonian :math:`\hat{H} = \sum \hat{Z}_k + \sum \hat{Z}_i \hat{Z}_j` with i,j running over the edges. .. GENERATED FROM PYTHON SOURCE LINES 216-241 .. code-block:: Python ham = [reduce(np.kron, [pauli_z] * n)] for i in range(1, n): op = np.array([identity] * n) op[0] = pauli_z op[i] = pauli_z op = reduce(np.kron, op) ham.append(op) # Use yet again another optimizer for path contraction. class MyOptimizer(PathOptimizer): def __call__( self, inputs: Collection[object], output: object, size_dict: object, memory_limit: object = None ) -> list[tuple[int, int]]: return [(0, 1)] * (len(inputs) - 1) opt = MyOptimizer() # Define initial parameters, which will be optimized through running the algorithm. params = 2 * ANGLE_PI * rng.random(len(gamma) + len(beta)) # Run the classical optimizer and simulate the quantum circuit with TN backend. res = minimize(cost, params, args=(n, ham, iterations, len(gamma), opt), method="COBYLA") print(res.message) .. rst-class:: sphx-glr-script-out .. code-block:: none /home/docs/checkouts/readthedocs.org/user_builds/graphix/checkouts/v0.3.5/examples/tn_simulation.py:207: UserWarning: Default random-number generator is used. Results may not be reproducible. mbqc_tn = pattern.simulate_pattern(backend="tensornetwork", graph_prep="parallel") /home/docs/checkouts/readthedocs.org/user_builds/graphix/checkouts/v0.3.5/examples/tn_simulation.py:207: UserWarning: Default random-number generator is used. Results may not be reproducible. mbqc_tn = pattern.simulate_pattern(backend="tensornetwork", graph_prep="parallel") Return from COBYLA because the trust region radius reaches its lower bound. .. GENERATED FROM PYTHON SOURCE LINES 242-243 Finally, run the circuit once again with the optimized parameters. .. GENERATED FROM PYTHON SOURCE LINES 243-251 .. code-block:: Python circuit = Circuit(n) ansatz(circuit, n, res.x[: len(gamma)], res.x[len(gamma) :], iterations) pattern = circuit.transpile().pattern pattern.standardize() pattern.shift_signals() mbqc_tn = pattern.simulate_pattern(backend="tensornetwork", graph_prep="parallel") .. rst-class:: sphx-glr-script-out .. code-block:: none /home/docs/checkouts/readthedocs.org/user_builds/graphix/checkouts/v0.3.5/examples/tn_simulation.py:249: UserWarning: Default random-number generator is used. Results may not be reproducible. mbqc_tn = pattern.simulate_pattern(backend="tensornetwork", graph_prep="parallel") .. GENERATED FROM PYTHON SOURCE LINES 252-253 Let's use the defined optimizer and find the most probable basis states. .. GENERATED FROM PYTHON SOURCE LINES 253-261 .. code-block:: Python max_prob = 0 most_prob_state = 0 bars = [] for i in range(2**n): value = mbqc_tn.basis_amplitude(i) bars.append(value) .. GENERATED FROM PYTHON SOURCE LINES 262-263 Plot the output. .. GENERATED FROM PYTHON SOURCE LINES 263-272 .. code-block:: Python fig, ax = plt.subplots(figsize=(10, 5)) ax.bar(range(2**n), bars, color="maroon", width=0.2) ax.set_xticks(range(2**n)) ax.set_xlabel("States") ax.set_ylabel("Probabilites") ax.set_title("Measurement probabilities using the optimized parameters") plt.show() .. image-sg:: /gallery/images/sphx_glr_tn_simulation_003.png :alt: Measurement probabilities using the optimized parameters :srcset: /gallery/images/sphx_glr_tn_simulation_003.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 273-277 As we can see the most probable are 15 and 16 ( ``|11110>`` 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 279-281 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 281-292 .. code-block:: Python fig, ax = plt.subplots(ncols=2, figsize=(8, 6)) ax = ax.flatten() g: nx.Graph[int] = 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 25.572 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-jupyter :download:`Download Jupyter notebook: tn_simulation.ipynb ` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: tn_simulation.py ` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: tn_simulation.zip ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_