{ "cells": [ { "attachments": {}, "cell_type": "markdown", "id": "31fbfeda-e02b-4029-becb-dfd8cf8a2db0", "metadata": { "raw_mimetype": "text/restructuredtext" }, "source": [ "(quantum-circuit-example)=\n", "\n", "# Quantum Circuits\n", "\n", "`quimb` has powerful support for simulating quantum circuits via\n", "its ability to represent and contract arbitrary geometry tensor\n", "networks. However, because its representation is generally neither\n", "the full wavefunction (like many other simulators) or a specific\n", "TN (for example an MPS or PEPS like some other simulators), using\n", "it is a bit different and requires potentially extra thought.\n", "\n", "Specifically, the computational memory and effort is very sensitive\n", "to **what** you want to compute, but also how long you are willing\n", "to spend **computing how to compute it** - essentially, pre-processing.\n", "\n", ":::{note}\n", "All of which to say, you are unfortunately quite unlikely to achieve\n", "the best performance without some tweaking of the default arguments.\n", ":::\n", "\n", "Nonetheless, here's a quick preview of the kind of circuit that many classical\n", "simulators might struggle with - an 80 qubit GHZ-state prepared using a\n", "completely randomly ordered sequence of CNOTs:" ] }, { "cell_type": "code", "execution_count": 1, "id": "e9942fcd-455d-43f7-b4d1-00d6e850469c", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "11111111111111111111111111111111111111111111111111111111111111111111111101111111\n" ] } ], "source": [ "%config InlineBackend.figure_formats = ['svg']\n", "\n", "import random\n", "\n", "import quimb as qu\n", "import quimb.tensor as qtn\n", "\n", "N = 80\n", "circ = qtn.Circuit(N)\n", "\n", "# randomly permute the order of qubits\n", "regs = list(range(N))\n", "random.shuffle(regs)\n", "\n", "# hamadard on one of the qubits\n", "circ.apply_gate(\"H\", regs[0])\n", "\n", "# chain of cnots to generate GHZ-state\n", "for i in range(N - 1):\n", " circ.apply_gate(\"CNOT\", regs[i], regs[i + 1])\n", "\n", "# apply multi-controlled NOT\n", "circ.apply_gate(\"X\", regs[-1], controls=regs[:-1])\n", "\n", "# sample it a few times\n", "for b in circ.sample(1, seed=42):\n", " print(b)" ] }, { "cell_type": "markdown", "id": "bcedf1c9-7506-4006-b3e9-270c3abfb689", "metadata": {}, "source": [ "As mentioned above, various pre-processing steps need to occur (which will happen on the first run if not explicitly called). The results of these are cached such that the more you sample the more the simulation should speed up:" ] }, { "cell_type": "code", "execution_count": 2, "id": "24460891-8162-4b66-8703-88a32ef8552c", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "11111111111111111111111111111111111111111111111111111111111111111111111101111111\n", "00000000000000000000000000000000000000000000000000000000000000000000000000000000\n", "00000000000000000000000000000000000000000000000000000000000000000000000000000000\n", "11111111111111111111111111111111111111111111111111111111111111111111111101111111\n", "00000000000000000000000000000000000000000000000000000000000000000000000000000000\n", "00000000000000000000000000000000000000000000000000000000000000000000000000000000\n", "00000000000000000000000000000000000000000000000000000000000000000000000000000000\n", "00000000000000000000000000000000000000000000000000000000000000000000000000000000\n", "CPU times: user 449 ms, sys: 3.76 ms, total: 453 ms\n", "Wall time: 449 ms\n" ] } ], "source": [ "%%time\n", "# sample it 8 times\n", "for b in circ.sample(8, seed=43):\n", " print(b)" ] }, { "cell_type": "markdown", "id": "1586d1a4-3b3c-4d51-9eda-065f47041445", "metadata": {}, "source": [ "Collect some statistics:" ] }, { "cell_type": "code", "execution_count": 3, "id": "96770615-e231-4973-8af2-1fac694def49", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "CPU times: user 102 ms, sys: 3.04 ms, total: 105 ms\n", "Wall time: 101 ms\n" ] }, { "data": { "text/plain": [ "Counter({'11111111111111111111111111111111111111111111111111111111111111111111111101111111': 50,\n", " '00000000000000000000000000000000000000000000000000000000000000000000000000000000': 50})" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "%%time\n", "from collections import Counter\n", "\n", "# sample it 100 times, count results:\n", "Counter(circ.sample(100))" ] }, { "cell_type": "markdown", "id": "074234b6-860d-4446-b0a1-7ed4d42a1c23", "metadata": { "raw_mimetype": "text/restructuredtext" }, "source": [ ":::{hint}\n", "This doc mostly is relevant to the *exact* tensor network simulation of quantum circuits. For full examples including MPS and PEPS simulations see the following examples:\n", "\n", "- {ref}`ex-circuit-sampling`\n", "- {ref}`ex-bayesian-optimize-qaoa`\n", "- {ref}`example-tn-training-circuits`\n", "- {ref}`quantum-circuit-mps-example`\n", ":::\n", "\n", "\n", "## Simulation Steps\n", "\n", "Here's an overview of the general steps for an *exact* tensor network quantum circuit simulation:\n", "\n", "1. Build the **tensor network representation of the circuit**, this involves taking the initial state (by default the product state \\$ | 000 \\\\ldots 00 \\\\rangle \\$ ) and adding tensors representing the gates to it, possibly performing low-rank decompositions on them if beneficial.\n", "\n", "2. **Form the entire tensor network of the quantity** you actually want to compute, this might include:\n", "\n", " > - the full, dense, wavefunction (i.e. a single tensor)\n", " > - a local expectation value or reduced density matrix\n", " > - a marginal probability distribution to sample bitstrings from, mimicking a real quantum computer (this is what is happening above)\n", " > - the fidelity with a target state or unitary, maybe to use automatic differentation to train the parameters of a given circuit to perform a specific task\n", "\n", "3. Perform **local simplifications on the tensor network** to make it easier (possibly trivial!) to contract. This step, whilst efficient in the complexity sense, can still introduce some significant overhead.\n", "\n", "4. Find a **contraction path** for this simplified tensor network. This a series of pairwise tensor contractions that turn the tensor network into a single tensor - represented by a binary *contraction tree*. The memory required for the intermediate tensors can be checked in advance at this stage.\n", "\n", "5. Optionally **slice (or 'chunk') the contraction**, breaking it into many independent, smaller contractions, either to fit memory constraints or introduce embarassing parallelism.\n", "\n", "6. **Perform the contraction!** Up until this point the tensors are generally very small and so can be easily passed to some other library with which to perform the actual contraction (for example, one with GPU support).\n", "\n", ":::{warning}\n", "The overall computational effort memory required in this last step is\n", "**very** sensitive (we are talking possibly *orders and order of magnitude*)\n", "to how well one finds the so-called 'contraction path' or 'contraction tree'\n", "\\- which itself can take some effort. The overall simulation is thus a\n", "careful balancing of time spent (a) simplifying (b) path finding, and\n", "(c) contracting.\n", ":::\n", "\n", ":::{note}\n", "It's also important to note that this last step is where the exponential slow-down\n", "expected for generic quantum circuits will appear. Unless the circuit is trivial in\n", "some way, the tensor network simplification and path finding can only ever shave\n", "off a (potentially very significant) prefactor from an underlying exponential scaling.\n", ":::" ] }, { "cell_type": "markdown", "id": "f2066f2d-cf4d-408f-92ae-1d5d07585bb2", "metadata": { "raw_mimetype": "text/restructuredtext" }, "source": [ "## Building the `Circuit`\n", "\n", "The main relevant object is {class}`~quimb.tensor.circuit.Circuit`.\n", "Under the hood this uses {func}`~quimb.tensor.tn1d.core.gate_TN_1D`, which\n", "applies an operator on some number of sites to any notionally 1D tensor network\n", "(not just an MPS), whilst maintaining the outer indices (e.g. `'k0', 'k1', 'k2', ...`).\n", ". The various options for applying the operator\n", "and propagating tags to it (if not contracted in) can be found in\n", "{func}`~quimb.tensor.tn1d.core.gate_TN_1D`. Note that the '1D' nature of the\n", "TN is just for indexing, gates can be applied to arbitrary combinations of\n", "sites within this 'register'.\n", "\n", "The following is a basic example of building a quantum circuit TN by applying\n", "a variety of gates to, for visualization purposes, nearest neighbors in a chain." ] }, { "cell_type": "code", "execution_count": 4, "id": "5198f13a-6e4f-4b0b-8411-e1ae3fb68d37", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 10 qubits and tag the initial wavefunction tensors\n", "circ = qtn.Circuit(N=10)\n", "\n", "# initial layer of hadamards\n", "for i in range(10):\n", " circ.apply_gate(\"H\", i, gate_round=0)\n", "\n", "# 8 rounds of entangling gates\n", "for r in range(1, 9):\n", " # even pairs\n", " for i in range(0, 10, 2):\n", " circ.apply_gate(\"CX\", i, i + 1, gate_round=r)\n", "\n", " # Y-rotations\n", " for i in range(10):\n", " circ.apply_gate(\"RZ\", 1.234, i, gate_round=r)\n", "\n", " # odd pairs\n", " for i in range(1, 9, 2):\n", " circ.apply_gate(\"CZ\", i, i + 1, gate_round=r)\n", "\n", " # X-rotations\n", " for i in range(10):\n", " circ.apply_gate(\"RX\", 1.234, i, gate_round=r)\n", "\n", "# final layer of hadamards\n", "for i in range(10):\n", " circ.apply_gate(\"H\", i, gate_round=r + 1)\n", "\n", "circ" ] }, { "cell_type": "markdown", "id": "f1fa4bbc-270f-444a-8f03-c8fbf4e92f19", "metadata": {}, "source": [ "The basic tensor network representing the state is stored in the\n", "``.psi`` attribute, which we can then visualize:" ] }, { "cell_type": "code", "execution_count": 5, "id": "f52d1a19-8629-4d1c-b7f0-91f4bcc9e8ed", "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "" ], "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "circ.psi.draw(color=[\"PSI0\", \"H\", \"CX\", \"RZ\", \"RX\", \"CZ\"])" ] }, { "cell_type": "markdown", "id": "039cb9b1-32c3-46f2-a5a4-a47a2b6f0c99", "metadata": {}, "source": [ "Note by default the CNOT and CZ gates have\n", "been split via a rank-2 spatial decomposition into two parts acting on each site seperately\n", "but connected by a new bond.\n", "We can also graph the default (``propagate_tags='register'``) method for\n", "adding site tags to the applied operators:" ] }, { "cell_type": "code", "execution_count": 6, "id": "e919f266-8c89-419a-9cb5-62a15a19df75", "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "" ], "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "circ.psi.draw(color=[f\"I{i}\" for i in range(10)])" ] }, { "cell_type": "markdown", "id": "3f65f883-78d0-4132-a434-bf08b0bc3c4d", "metadata": {}, "source": [ "Or since we supplied ``gate_round`` as an keyword (which is optional), the tensors\n", "are also tagged in that way:" ] }, { "cell_type": "code", "execution_count": 7, "id": "b7041e14-1935-4af0-af18-8040c425eeec", "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "" ], "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "circ.psi.draw(color=[\"PSI0\"] + [f\"ROUND_{i}\" for i in range(10)])" ] }, { "cell_type": "markdown", "id": "f13c177e-7e6a-42d1-a64a-1e9e82753d29", "metadata": {}, "source": [ "All of these might be helpful when addressing only certain tensors:" ] }, { "cell_type": "code", "execution_count": 8, "id": "dcd9ac7c-3b83-47cf-a69f-001c69c9a20c", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
TensorNetworkGenVector(tensors=1, indices=3)
Tensor(shape=(2, 2, 2), inds=[_9c007fAACBT, _9c007fAACBg, _9c007fAACBS], tags={GATE_69, ROUND_3, CX, I3}),backend=numpy, dtype=complex128, data=array([[[ 0.84089642+0.j, 0. +0.j],\n", " [ 0. +0.j, 0.84089642+0.j]],\n", "\n", " [[-0. +0.j, -0.84089642+0.j],\n", " [-0.84089642+0.j, 0. +0.j]]])
" ], "text/plain": [ "TensorNetworkGenVector(tensors=1, indices=3)" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# select the subnetwork of tensors with *all* following tags\n", "circ.psi.select([\"CX\", \"I3\", \"ROUND_3\"], which=\"all\")" ] }, { "cell_type": "markdown", "id": "888c7a46-c2ca-4e11-92d4-54c584878ab3", "metadata": { "raw_mimetype": "text/restructuredtext" }, "source": [ ":::{note}\n", "The tensor(s) of each gate is/are also individually tagged like\n", "`[f'GATE_{g}' for g in range(circ.num_gates)]`.\n", ":::\n", "\n", "The full list of currently implemented gates is here:" ] }, { "cell_type": "code", "execution_count": 9, "id": "4ce7cc04-6be0-43d0-a0f2-e2604f613c2f", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "CCNOT\n", "CCX\n", "CCY\n", "CCZ\n", "CNOT\n", "CPHASE\n", "CRX\n", "CRY\n", "CRZ\n", "CSWAP\n", "CU1\n", "CU2\n", "CU3\n", "CX\n", "CY\n", "CZ\n", "FREDKIN\n", "FS\n", "FSIM\n", "FSIMG\n", "GIVENS\n", "GIVENS2\n", "H\n", "HZ_1_2\n", "IDEN\n", "IS\n", "ISWAP\n", "PHASE\n", "RX\n", "RXX\n", "RY\n", "RYY\n", "RZ\n", "RZZ\n", "S\n", "SDG\n", "SU4\n", "SWAP\n", "SX\n", "SXDG\n", "T\n", "TDG\n", "TOFFOLI\n", "U1\n", "U2\n", "U3\n", "W_1_2\n", "X\n", "XXMINUSYY\n", "XXPLUSYY\n", "X_1_2\n", "Y\n", "Y_1_2\n", "Z\n", "Z_1_2\n" ] } ], "source": [ "print(\"\\n\".join(sorted(qtn.circuit.ALL_GATES)))" ] }, { "cell_type": "markdown", "id": "bf18f25c-8faa-4bcb-8af0-f1cf0c8c8f03", "metadata": { "raw_mimetype": "text/restructuredtext" }, "source": [ "## Parametrized Gates\n", "\n", "Of these gates, any which take parameters - `['RX', 'RY', 'RZ', 'U3', 'FSIM', 'RZZ', ...]` - can\n", "be 'parametrized', which adds the gate to the network as a\n", "{class}`~quimb.tensor.tensor_core.PTensor`. The main use of this is that when optimizing\n", "a TN, for example, the parameters that generate the tensor data will be optimized rather\n", "than the tensor data itself." ] }, { "cell_type": "code", "execution_count": 10, "id": "dc3442d1-a015-465b-9c34-266f65051482", "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "" ], "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "circ_param = qtn.Circuit(6)\n", "\n", "for l in range(3):\n", " for i in range(0, 6, 2):\n", " circ_param.apply_gate(\n", " \"FSIM\",\n", " random.random(),\n", " random.random(),\n", " i,\n", " i + 1,\n", " parametrize=True,\n", " contract=False,\n", " )\n", " for i in range(1, 5, 2):\n", " circ_param.apply_gate(\n", " \"FSIM\",\n", " random.random(),\n", " random.random(),\n", " i,\n", " i + 1,\n", " parametrize=True,\n", " contract=False,\n", " )\n", " for i in range(6):\n", " circ_param.apply_gate(\n", " \"U3\",\n", " random.random(),\n", " random.random(),\n", " random.random(),\n", " i,\n", " parametrize=True,\n", " )\n", "\n", "circ_param.psi.draw(color=[\"PSI0\", \"FSIM\", \"U3\"])" ] }, { "cell_type": "markdown", "id": "ab4bdab1-8343-4ab0-9711-1acc9f7883bc", "metadata": {}, "source": [ "We've used the ``contract=False`` option which doesn't try and split the gate tensor in any way,\n", "so here there is now a single tensor per two qubit gate.\n", "In fact, for ``'FSIM'`` and random parameters there is no low-rank decomposition that would happen\n", "anyway, but this is also the only mode compatible with parametrized tensors:" ] }, { "cell_type": "code", "execution_count": 11, "id": "618d5dec-fdf5-4037-84ab-1dda8c5541af", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
PTensor(shape=(2, 2, 2, 2), inds=[_9c007fAACHE, _9c007fAACHA, _9c007fAACGu, _9c007fAACGv], tags={GATE_0, FSIM, I0, I1}),backend=numpy, dtype=None, data=array([[[[1. +0.j , 0. +0.j ],\n", " [0. +0.j , 0. +0.j ]],\n", "\n", " [[0. +0.j , 0.86371383+0.j ],\n", " [0. -0.50398255j, 0. +0.j ]]],\n", "\n", "\n", " [[[0. +0.j , 0. -0.50398255j],\n", " [0.86371383+0.j , 0. +0.j ]],\n", "\n", " [[0. +0.j , 0. +0.j ],\n", " [0. +0.j , 0.62645978-0.77945375j]]]])
" ], "text/plain": [ "PTensor(shape=(2, 2, 2, 2), inds=('_9c007fAACHE', '_9c007fAACHA', '_9c007fAACGu', '_9c007fAACGv'), tags=oset(['GATE_0', 'FSIM', 'I0', 'I1']), left_inds=('_9c007fAACGu', '_9c007fAACGv'))" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "circ_param.psi[\"GATE_0\"]" ] }, { "cell_type": "markdown", "id": "288ee59b-4914-4110-9b24-c1961b9be71d", "metadata": {}, "source": [ "For most tasks like contraction these are transparently handled like normal tensors:" ] }, { "cell_type": "code", "execution_count": 12, "id": "16e2a04f-3830-4e29-a1bf-f6dea1ded19c", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(-0.006284609650066678+0.002544768974237254j)" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "circ_param.amplitude(\"101001\")" ] }, { "cell_type": "markdown", "id": "da59b46f-9fed-46eb-b025-1a24741b250b", "metadata": { "raw_mimetype": "text/restructuredtext" }, "source": [ "## Forming the Target Tensor Network\n", "\n", "You can access the wavefunction tensor network\n", "$U |0\\rangle^{\\otimes{N}}$\n", "or more generally\n", "$U |\\psi_0\\rangle$\n", "with `Circuit.psi`\n", "or just the unitary,\n", "$U$,\n", "with `Circuit.uni`, and then manipulate and contract these yourself.\n", "However, there are built-in methods for constructing and contracting\n", "the tensor network to perform various common tasks.\n", "\n", "### Compute an amplitude\n", "\n", "\n", "\n", "- [Circuit.amplitude](quimb.tensor.circuit.Circuit.amplitude)\n", "\n", "This computes a single wavefunction amplitude coefficient, or transition amplitude:\n", "\n", "$$\n", "c_x = \\langle x | U | \\psi_0 \\rangle\n", "$$\n", "\n", "with, $x=0101010101 \\ldots$, for example. The probability of sampling $x$ from this circuit is $|c_x|^2$.\n", "\n", "**Example usage:**" ] }, { "cell_type": "code", "execution_count": 13, "id": "88e01279-272f-4e18-bd36-54a8891afdcc", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(-0.0062675896452940106+0.012702244544177505j)" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "circ.amplitude(\"0101010101\")" ] }, { "cell_type": "markdown", "id": "0630c5a7-3c86-4593-8caf-cf38342bf6e9", "metadata": { "raw_mimetype": "text/restructuredtext" }, "source": [ "### Compute a local expectation\n", "\n", "\n", "\n", "- [`Circuit.local_expectation`](quimb.tensor.circuit.Circuit.local_expectation)\n", "\n", "For an operator $G_{\\bar{q}}$ acting on qubits $\\bar{q}$, this computes:\n", "\n", "$$\n", "\\langle \\psi_{\\bar{q}} | G_{\\bar{q}} | \\psi_{\\bar{q}} \\rangle\n", "$$\n", "\n", "where $\\psi_{\\bar{q}}$ is the circuit wavefunction but only with gates which are\n", "in the 'reverse lightcone' (i.e. the causal cone) of qubits $\\bar{q}$. In the picture\n", "above the gates which we know cancel to the identity have been greyed out (and removed from the\n", "TN used).\n", "\n", "**Example usage:**" ] }, { "cell_type": "code", "execution_count": 14, "id": "65273dfe-ed10-47f1-9b7d-dfd0725f0a52", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "-0.018188965185459923" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "circ.local_expectation(qu.pauli(\"Z\") & qu.pauli(\"Z\"), (4, 5))" ] }, { "cell_type": "markdown", "id": "e49efbbf-8080-4e61-9679-c7c11e152d40", "metadata": {}, "source": [ "You can compute several individual expectations on the same sites by supplying a list (they are computed in a single contraction):" ] }, { "cell_type": "code", "execution_count": 15, "id": "9c16ca45-3d57-44f8-87fd-468639f46ee4", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "((-0.0057847192590983944+8.673617379884035e-18j),\n", " (0.058901881679250326-1.0625181290357943e-17j),\n", " (-0.01818896518545872+4.163336342344337e-17j))" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "circ.local_expectation(\n", " [\n", " qu.pauli(\"X\") & qu.pauli(\"X\"),\n", " qu.pauli(\"Y\") & qu.pauli(\"Y\"),\n", " qu.pauli(\"Z\") & qu.pauli(\"Z\"),\n", " ],\n", " where=(4, 5),\n", ")" ] }, { "cell_type": "markdown", "id": "b6a4e492-ae83-4adb-9777-50983a1cdd92", "metadata": { "raw_mimetype": "text/restructuredtext" }, "source": [ "### Compute a reduced density matrix\n", "\n", "\n", "\n", "- [`Circuit.partial_trace`](quimb.tensor.circuit.Circuit.partial_trace)\n", "\n", "This similarly takes a subset of the qubits, $\\bar{q}$, and contracts the\n", "wavefunction with its bra, but now only the qubits outside of $\\bar{q}$,\n", "producing a reduced density matrix:\n", "\n", "$$\n", "\\rho_{\\bar{q}} = \\mathrm{Tr}_{\\bar{p}} | \\psi_{\\bar{q}} \\rangle \\langle \\psi_{\\bar{q}} |\n", "$$\n", "\n", "where the partial trace is over $\\bar{p}$, the complementary set of qubits to $\\bar{q}$.\n", "Obviously once you have $\\rho_{\\bar{q}}$ you can compute many different local expectations\n", "and so it can be more efficient than repeatedly calling\n", "{meth}`~quimb.tensor.circuit.Circuit.local_expectation`.\n", "\n", "**Example usage:**" ] }, { "cell_type": "code", "execution_count": 16, "id": "6f8b7a46-103f-4dcb-9d04-eafb995d3653", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[ 0.252+0.j , 0.013+0.011j, -0.019+0.007j, -0.016-0.003j],\n", " [ 0.013-0.011j, 0.255+0.j , 0.013+0.014j, 0.02 +0.017j],\n", " [-0.019-0.007j, 0.013-0.014j, 0.254+0.j , 0.019+0.012j],\n", " [-0.016+0.003j, 0.02 -0.017j, 0.019-0.012j, 0.239+0.j ]])" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "circ.partial_trace((4, 5)).round(3)" ] }, { "cell_type": "markdown", "id": "de9818d4-d8ea-4c2c-b5c9-3cb66ca86116", "metadata": { "raw_mimetype": "text/restructuredtext" }, "source": [ "### Compute a marginal probability distribution\n", "\n", "\n", "\n", "- {meth}`~quimb.tensor.circuit.Circuit.compute_marginal`\n", "\n", "This method computes the probability distribution over some qubits, $\\bar{q}$, conditioned on some\n", "partial fixed result on qubits $\\bar{f}$ (which can be no qubits).\n", "\n", "$$\n", "p(\\bar{q} | x_{\\bar{f}}) =\n", "\\mathrm{diag}\n", "\\mathrm{Tr}_{\\bar{p}}\n", "\\langle x_{\\bar{f}} |\n", "\\psi_{\\bar{f} \\cup \\bar{q}} \\rangle \\langle \\psi_{\\bar{f} \\cup \\bar{q}}\n", "| x_{\\bar{f}} \\rangle\n", "$$\n", "\n", "Here only the causal cone relevant to $\\bar{f} \\cup \\bar{q}$ is needed, with the remaining\n", "qubits, $\\bar{p}$ being traced out. We directly take the diagonal within the contraction\n", "using hyper-indices (depicted as a COPY-tenso above) to avoid forming the full reduced density matrix.\n", "The result is a $2^{|\\bar{q}|}$ dimensional tensor containing the probabilites for each\n", "bit-string $x_{\\bar{q}}$, given that we have already 'measured' $x_{\\bar{f}}$.\n", "\n", "**Example usage:**" ] }, { "cell_type": "code", "execution_count": 17, "id": "5a9db911-0e0e-4455-8178-e86c92f48a2d", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[0.03422455, 0.02085596],\n", " [0.03080204, 0.02780321]])" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "p = circ.compute_marginal(\n", " (1, 2), fix={0: \"1\", 3: \"0\", 4: \"1\"}, dtype=\"complex128\"\n", ")\n", "p" ] }, { "cell_type": "code", "execution_count": 18, "id": "2b1fe1ba-e83f-422e-848d-b2d5abbfbd3f", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'10'" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "qtn.circuit.sample_bitstring_from_prob_ndarray(p / p.sum())" ] }, { "cell_type": "markdown", "id": "96df885d-510c-4052-bbcd-7a8ece61af04", "metadata": { "raw_mimetype": "text/restructuredtext" }, "source": [ "### Generate unbiased samples\n", "\n", "\n", "\n", "- [`Circuit.sample`](quimb.tensor.circuit.Circuit.sample)\n", "\n", "The main use of [`Circuit.compute_marginal`](quimb.tensor.circuit.Circuit.compute_marginal) is as a subroutine used to generate\n", "unbiased samples from circuits. We first pick some group of qubits, $\\bar{q_A}$ to 'measure', then\n", "condition on the resulting bitstring $x_{\\bar{q_A}}$, to compute the marginal on the next group of qubits\n", "$\\bar{q_B}$ and so forth. Eventually we reach the 'final marginal' where we no longer need to trace\n", "any qubits out, so instead we can compute:\n", "\n", "$$\n", "p(\\bar{q_Z} | x_{\\bar{q_A}} x_{\\bar{q_B}} \\ldots) =\n", "|\\langle x_{\\bar{q_A}} x_{\\bar{q_B}} \\ldots | \\psi \\rangle|^2\n", "$$\n", "\n", "since the 'bra' representing the partial bit-string only acts on some of the qubits this object is still a\n", "$2^{|\\bar{q}|}$ dimensional tensor, which we sample from to get the final bit-string $x_{\\bar{q_z}}$.\n", "The overall sample generated is then the concatenation of all these bit-strings:\n", "\n", "$$\n", "x_{\\bar{q_A}} x_{\\bar{q_B}} \\ldots x_{\\bar{q_Z}}\n", "$$\n", "\n", "As such, to generate a sample once we have put our qubits into $N_g$ groups, we need to perform $N_g$\n", "contractions. The contractions near the beginning are generally easier since we only need the causal cone for\n", "a small number of qubits, and the contractions towards the end are easier since we have largely or fully severed\n", "the bonds between the ket and bra by conditioning.\n", "\n", "This is generally more expensive than computing local quantities but there are a couple of reprieves:\n", "\n", "1. **Because of causality we are free to choose the order and groupings of the qubits in whichever way is most efficient.**\n", "\n", "The automatic choice is to start at the qubit(s) with the smallest reverse lightcone and greedily expand (see section below).\n", "Grouping the qubits together can have a large beneficial impact on overall computation time, but imposes a hard upper limit\n", "on the required memory like $2^{|\\bar{q}|}$.\n", "\n", ":::{note}\n", "You can set the group size to be that of the whole sytem, which is equivalent to sampling from the full wavefunction,\n", "if you want to do this, it would be more efficient to call\n", "[`Circuit.simulate_counts`](quimb.tensor.circuit.Circuit.simulate_counts), which\n", "doesn't draw the samples individually.\n", ":::\n", "\n", "2. **Once we have computed a particular marginal we can cache the result, meaning if we come across the same sub-string result,\n", " we don't need to contract anything again, the trivial example being the first marginal we compute.**\n", "\n", "\n", "\n", "The second point is easy to understand if we think of the sampling process as the repeated exploration of a\n", "probability tree as above - which is shown for 3 qubits grouped individually, with a first sample of $011$ drawn.\n", "If the next sample we drew was $010$ we wouldn't have to perform any more contractions, since we'd be following\n", "already explored branches. In the extreme case of the GHZ-state at the top, there are only two branches, so once we\n", "have generated the all-zeros and the all-ones result there we won't need to perform any more contractions.\n", "\n", "**Example usage:**" ] }, { "cell_type": "code", "execution_count": 19, "id": "c4f50cba-1a87-48b6-a073-d578e3a90a45", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1111001101\n", "0001010000\n", "0000011110\n", "1100110000\n", "1001000111\n", "1000111000\n", "0101010100\n", "1000001001\n", "0101010011\n", "0010101111\n" ] } ], "source": [ "for b in circ.sample(10):\n", " print(b)" ] }, { "cell_type": "markdown", "id": "8c76939e-35d2-4fca-b75e-bceeaf914cc1", "metadata": {}, "source": [ "### Generate unbiased samples using the *'gate-by-gate'* method\n", "\n", "\n", "\n", "- [`Circuit.sample_gate_by_gate`](quimb.tensor.circuit.Circuit.sample_gate_by_gate)\n", "\n", "There is an alternative way to compute unbiased samples first introduced in *\"How to simulate quantum measurement without computing marginals\"* by Sergey Bravyi, David Gosset, Yinchen Liu (https://arxiv.org/abs/2112.08499). As illustrated above this essentially propagates a bit-string by sequentially adding more and more gates from the target circuit, and resampling parts of the bit-string corresponding to qubits that are acted on by the added gates. The upshot is that every contraction is no more expensive than computing a single amplitude, though the number of contractions we have to do scales linearly with the depth of the circuit. In many cases the theoretical total flop count of this 'gate-by-gate' method, as compared to the 'qubit-by-qubit' approach above, is significantly better, though practically the computation can still be dominated by subleading steps such as TN simplification - so it is worth comparing the two explicitly!\n", "\n", "This method makes use of being to reorder the gates so that groups of adjacent qubits are acted in in close succession. If you call the gate by gate rehearse methods you can access the sequence of circuits used.\n", "\n", "**Example usage:**" ] }, { "cell_type": "code", "execution_count": 20, "id": "5bb08c38-9b0c-41b8-ab34-ff9aa24c5c97", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0011001011\n", "1010010111\n", "1100100001\n", "1110110111\n", "1001000001\n", "0000110010\n", "0101110000\n", "0101100000\n", "0101101100\n", "1011100100\n" ] } ], "source": [ "for b in circ.sample_gate_by_gate(10):\n", " print(b)" ] }, { "cell_type": "markdown", "id": "f13d33df-62c0-41a7-93ba-82aec81ed19d", "metadata": { "raw_mimetype": "text/restructuredtext" }, "source": [ "### Generate samples from a chaotic circuit\n", "\n", "\n", "\n", "- [`Circuit.sample_chaotic`](quimb.tensor.circuit.Circuit.sample_chaotic)\n", "\n", "Some circuits can be assumed to produce chaotic results, and a useful property of these is that if you remove (partially trace)\n", "a certain number of qubits, the remaining marginal probability distribution is close to uniform.\n", "This is like saying as we travel along the probability tree depicted above, the probabilities are all very similar\n", "until we reach 'the last few qubits', whose marginal distribution then depends sensitively on the bit-string generated\n", "so far.\n", "\n", "If we know roughly what number of qubits suffices for this property to hold, $m$, we can uniformly sample bit-strings for the first\n", "$f = N - m$ qubits then we only need to contract the 'final marginal' from above.\n", "In other words, we only need to compute and sample from:\n", "\n", "$$\n", "p( \\bar{m} | x_{\\bar{f}} ) = |\\langle x_{\\bar{f}} | \\psi \\rangle|^2\n", "$$\n", "\n", "Where $\\bar{m}$ is the set of marginal qubits, and $\\bar{f}$ is the set of qubits fixed to a random bit-string.\n", "If $m$ is not too large, this is generally a very similar cost to that of computing a single amplitude.\n", "\n", ":::{note}\n", "This task is the relevant method for classically simulating the results in [\"Quantum supremacy using a programmable\n", "superconducting processor\"](https://www.nature.com/articles/s41586-019-1666-5).\n", ":::\n", "\n", "**Example usage:**" ] }, { "cell_type": "code", "execution_count": 21, "id": "bd141fe5-5cf4-452f-8de8-552855d86f72", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0011110100\n", "0001000010\n", "0011110100\n", "0110000011\n", "0000011000\n", "1101111011\n", "1111011010\n", "1110000101\n", "1111010101\n", "0010110000\n" ] } ], "source": [ "for b in circ.sample_chaotic(10, marginal_qubits=5):\n", " print(b)" ] }, { "cell_type": "markdown", "id": "9f290c32-c62e-4fd7-85bd-c0b9f48ef7a0", "metadata": {}, "source": [ "Five of these qubits will now be sampled completely randomly." ] }, { "cell_type": "markdown", "id": "94dd7bef-873f-46f4-b262-169af327ef81", "metadata": { "raw_mimetype": "text/restructuredtext" }, "source": [ "### Compute the dense vector representation of the state\n", "\n", "\n", "\n", "- {meth}`~quimb.tensor.circuit.Circuit.to_dense`\n", "\n", "In other words just contract the core `circ.psi` object into a single tensor:\n", "\n", "$$\n", "U | \\psi_0 \\rangle \\rightarrow |\\psi_{\\mathrm{dense}}\\rangle\n", "$$\n", "\n", "Where $|\\psi_{\\mathrm{dense}}\\rangle$ is a column vector.\n", "Unlike other simulators however, the contraction order here isn't defined by the order the gates were applied in,\n", "meaning the full wavefunction does not neccessarily need to be formed until the last few contractions.\n", "\n", ":::{hint}\n", "For small to medium circuits, the benefits of doing this as compared with standard, 'Schrodinger-style'\n", "simulation might be negligible (since the overall scaling is still limited by the number of qubits).\n", "Indeed the savings are likely outweighed by the pre-processing step's overhead if you are only running\n", "the circuit geometry once.\n", ":::\n", "\n", "**Example usage:**" ] }, { "cell_type": "code", "execution_count": 22, "id": "1b4f3d41-b677-4f43-928e-a773253f708d", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[[ 0.022278+0.044826j]\n", " [ 0.047567+0.001852j]\n", " [-0.028239+0.01407j ]\n", " ...\n", " [ 0.016 -0.008447j]\n", " [-0.025437-0.015225j]\n", " [-0.033285-0.030653j]]" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "circ.to_dense()" ] }, { "cell_type": "markdown", "id": "37b4a489-2570-49a2-8549-2f2ede26993e", "metadata": { "raw_mimetype": "text/restructuredtext" }, "source": [ "### Rehearsals\n", "\n", "Each of the above methods can perform a trial run, where the tensor networks and contraction paths are generated\n", "and intermediates possibly cached, but the main contraction is not performed. Either supply `rehearse=True` or use\n", "the corresponding partial methods:\n", "\n", "- [`Circuit.amplitude_rehearse`](quimb.tensor.circuit.Circuit.amplitude_rehearse)\n", "- [`Circuit.local_expectation_rehearse`](quimb.tensor.circuit.Circuit.local_expectation_rehearse)\n", "- [`Circuit.partial_trace_rehearse`](quimb.tensor.circuit.Circuit.partial_trace_rehearse)\n", "- [`Circuit.compute_marginal_rehearse`](quimb.tensor.circuit.Circuit.compute_marginal_rehearse)\n", "- [`Circuit.sample_rehearse`](quimb.tensor.circuit.Circuit.sample_rehearse)\n", "- [`Circuit.sample_gate_by_gate_rehearse`](quimb.tensor.circuit.Circuit.sample_gate_by_gate_rehearse)\n", "- [`Circuit.sample_chaotic_rehearse`](quimb.tensor.circuit.Circuit.sample_chaotic_rehearse)\n", "- [`Circuit.to_dense_rehearse`](quimb.tensor.circuit.Circuit.to_dense_rehearse)\n", "\n", "These each return a `dict` with the tensor network that *would* be\n", "contracted in the main part of the computation (with the key `'tn'`),\n", "and the {class}`cotengra.ContractionTree` object describing the contraction\n", "path found for that tensor network (with the key `'tree'`). For example:" ] }, { "cell_type": "code", "execution_count": 23, "id": "cb933e4d-e142-4b23-945c-f7c9e4891342", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "7.0" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "rehs = circ.amplitude_rehearse()\n", "\n", "# contraction width\n", "W = rehs[\"tree\"].contraction_width()\n", "W" ] }, { "cell_type": "markdown", "id": "32b9223e-b760-421f-b81b-3d0a1762a03f", "metadata": {}, "source": [ "Upper twenties is the limit for standard (~10GB) amounts of RAM." ] }, { "cell_type": "code", "execution_count": 24, "id": "62671971-3f3e-4a61-bf44-1b26ba01ac81", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3.911902996044032" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# contraction cost\n", "# N.B.\n", "# * 2 to get real dtype FLOPs\n", "# * 8 to get complex dtype FLOPs (relevant for most QC)\n", "C = rehs[\"tree\"].contraction_cost(log=10)\n", "C" ] }, { "cell_type": "code", "execution_count": 25, "id": "58298e3d-1af0-45af-a707-0d4c693b9e7e", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(0.9678201993158628-5.323746319985984j)" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# perform contraction\n", "rehs[\"tn\"].contract(all, optimize=rehs[\"tree\"], output_inds=())" ] }, { "cell_type": "markdown", "id": "b011f251-aae9-4e78-93a6-0177b0dbc730", "metadata": { "raw_mimetype": "text/restructuredtext" }, "source": [ "{meth}`~quimb.tensor.circuit.Circuit.sample_rehearse`\n", "and\n", "{meth}`~quimb.tensor.circuit.Circuit.sample_chaotic_rehearse`\n", "both return a dict of dicts, where the keys of the top dict\n", "are the (ordered) groups of marginal qubits used, and the values\n", "are the rehearsal dicts as above." ] }, { "cell_type": "code", "execution_count": 26, "id": "53c2ae2d-5282-48da-9cb7-b7d4ea77c178", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "dict_keys([(0, 1, 2), (3, 4, 9), (5, 6, 7), (8,)])" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "rehs = circ.sample_rehearse(group_size=3)\n", "rehs.keys()" ] }, { "cell_type": "code", "execution_count": 27, "id": "cf341e1c-38b4-4ccc-8a05-1b7432aa9b0d", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "dict_keys(['tn', 'tree', 'W', 'C'])" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "rehs[(3, 4, 9)].keys()" ] }, { "cell_type": "markdown", "id": "2197b25d-9e17-452a-af25-c71ee7063a03", "metadata": { "raw_mimetype": "text/restructuredtext" }, "source": [ "### Unitary Reverse Lightcone Cancellation\n", "\n", "In several of the examples above we made use of 'reverse lightcone', or the set of gates\n", "that have a causal effect on a particular set of output qubits, $\\bar{q}$, to work\n", "with a potentially much smaller TN representation of the wavefunction:\n", "\n", "$$\n", "| \\psi_{\\bar{q}} \\rangle\n", "$$\n", "\n", "This can simply be understood as cancellation of the gate unitaries at the boundary where\n", "the bra and ket meet:\n", "\n", "$$\n", "U^{\\dagger} U = \\mathcal{1}\n", "$$\n", "\n", "if there are no operators or projectors breaking this bond between the bra and ket.\n", "Whilst such simplifications can be found by the local simplifications (see below) its\n", "easier and quicker to drop these explicitly.\n", "\n", "You can see which gate tags are in the reverse lightcone of which regions of qubits by calling:" ] }, { "cell_type": "code", "execution_count": 28, "id": "d9d6b097-35ba-4470-ba10-003fa45c2e7c", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "('PSI0',\n", " 'GATE_0',\n", " 'GATE_1',\n", " 'GATE_2',\n", " 'GATE_3',\n", " 'GATE_4',\n", " 'GATE_5',\n", " 'GATE_6',\n", " 'GATE_7',\n", " 'GATE_8')" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# just show the first 10...\n", "lc_tags = circ.get_reverse_lightcone_tags(where=(0,))\n", "lc_tags[:10]" ] }, { "cell_type": "code", "execution_count": 29, "id": "da2012fa-9fef-44fa-ad7d-eb9c9a67b3fb", "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "" ], "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "circ.psi.draw(color=lc_tags)" ] }, { "cell_type": "markdown", "id": "c4190c50-a516-4b1f-ac6b-646674a796f8", "metadata": {}, "source": [ "We can plot the effect this has as selecting only these, $| \\psi \\rangle \\rightarrow | \\psi_{\\bar{q}} \\rangle$, on the norm with the following:" ] }, { "cell_type": "code", "execution_count": 30, "id": "96824aec-a768-4a0a-ba7b-ae8b5f2f547a", "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "" ], "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "# get the reverse lightcone wavefunction of qubit 0\n", "psi_q0 = circ.get_psi_reverse_lightcone(where=(0,))\n", "\n", "# plot its norm\n", "(psi_q0.H & psi_q0).draw(color=[\"PSI0\"] + [f\"ROUND_{i}\" for i in range(10)])" ] }, { "cell_type": "markdown", "id": "3715bbcb-47a9-4c65-b701-dc5e26e25d05", "metadata": { "raw_mimetype": "text/restructuredtext" }, "source": [ ":::{note}\n", "Although we have specified gate rounds here, this is not required to find the reverse\n", "lightcones, and indeed arbitrary geometry is handled too.\n", ":::" ] }, { "cell_type": "markdown", "id": "ff4c9f02-d48b-49b9-bbb5-8d152f78f952", "metadata": { "raw_mimetype": "text/restructuredtext" }, "source": [ "## Locally Simplifying the Tensor Network (the `simplify_sequence` kwarg)\n", "\n", "All of the main circuit methods take a `simplify_sequence` kwarg that controls local\n", "tensor network simplifications that are performed on the target TN object before\n", "the main contraction. The kwarg is a string of letters which is cycled through\n", "til convergence, which each letter corresponding to a different method:\n", "\n", "- `'A'`: [`TensorNetwork.antidiag_gauge`](quimb.tensor.tensor_core.TensorNetwork.antidiag_gauge)\n", "- `'D'`: [`TensorNetwork.diagonal_reduce`](quimb.tensor.tensor_core.TensorNetwork.diagonal_reduce)\n", "- `'C'`: [`TensorNetwork.column_reduce`](quimb.tensor.tensor_core.TensorNetwork.column_reduce)\n", "- `'R'`: [`TensorNetwork.rank_simplify`](quimb.tensor.tensor_core.TensorNetwork.rank_simplify)\n", "- `'S'`: [`TensorNetwork.split_simplify`](quimb.tensor.tensor_core.TensorNetwork.split_simplify)\n", "\n", "The final object thus both depends on which letters and the order specified\n", "-- `'ADCRS'` is the default.\n", "\n", "As an example, here is the `amplitude` tensor network of the circuit above,\n", "with only 'rank simplification' (contracting neighboring tensors that won't increase\n", "rank) performed:" ] }, { "cell_type": "code", "execution_count": 31, "id": "86c8d9f4-b86b-4700-b0ed-009a0a568985", "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "" ], "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "(\n", " circ\n", " # get the tensor network\n", " .amplitude_rehearse(simplify_sequence=\"R\")[\"tn\"]\n", " # plot it with each qubit register highlighted\n", " .draw(color=[f\"I{q}\" for q in range(10)])\n", ")" ] }, { "cell_type": "markdown", "id": "1ad19a0c-6c04-4fa9-a959-923e172577e1", "metadata": {}, "source": [ "You can see that only 3+ dimensional tensors remain. Now if we turn on all the\n", "simplification methods we get an even smaller tensor network:" ] }, { "cell_type": "code", "execution_count": 32, "id": "188da222-de38-45bc-a01d-50471de07a78", "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "" ], "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "(\n", " circ\n", " # get the tensor network\n", " .amplitude_rehearse(simplify_sequence=\"ADCRS\")[\"tn\"]\n", " # plot it with each qubit register highlighted\n", " .draw(color=[f\"I{q}\" for q in range(10)])\n", ")" ] }, { "cell_type": "markdown", "id": "2d668dd5-6780-4d54-bf8f-91b2ef915ebb", "metadata": { "raw_mimetype": "text/restructuredtext" }, "source": [ "And we also now have *hyper-indices* - indices shared by more than two tensors - that have been\n", "introduced by the [`TensorNetwork.diagonal_reduce`](quimb.tensor.tensor_core.TensorNetwork.diagonal_reduce) method.\n", "\n", ":::{hint}\n", "Of the five methods, only [`TensorNetwork.rank_simplify`](quimb.tensor.tensor_core.TensorNetwork.rank_simplify) doesn't\n", "require looking inside the tensors at the sparsity structure. This means that, at least for the moment,\n", "it is the only method that can be back-propagated through, for example.\n", ":::\n", "\n", "The five methods combined can have a significant effect on the complexity of the main TN to be contracted,\n", "in the most extreme case they can reduce a TN to a scalar:" ] }, { "cell_type": "code", "execution_count": 33, "id": "16ae7e1c-994b-49c3-9aff-42859dfb46e9", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
TensorNetworkGen(tensors=668, indices=802)
Tensor(shape=(2), inds=[_9c007fAABzP], tags={I0, PSI0}),backend=numpy, dtype=complex128, data=array([1.-0.j, 0.-0.j])
Tensor(shape=(2), inds=[_9c007fAABzQ], tags={I1, PSI0}),backend=numpy, dtype=complex128, data=array([1.-0.j, 0.-0.j])
Tensor(shape=(2), inds=[_9c007fAABzR], tags={I2, PSI0}),backend=numpy, dtype=complex128, data=array([1.-0.j, 0.-0.j])
Tensor(shape=(2), inds=[_9c007fAABzS], tags={I3, PSI0}),backend=numpy, dtype=complex128, data=array([1.-0.j, 0.-0.j])
Tensor(shape=(2), inds=[_9c007fAABzT], tags={I4, PSI0}),backend=numpy, dtype=complex128, data=array([1.-0.j, 0.-0.j])
Tensor(shape=(2), inds=[_9c007fAABzU], tags={I5, PSI0}),backend=numpy, dtype=complex128, data=array([1.-0.j, 0.-0.j])
Tensor(shape=(2), inds=[_9c007fAABzV], tags={I6, PSI0}),backend=numpy, dtype=complex128, data=array([1.-0.j, 0.-0.j])
Tensor(shape=(2), inds=[_9c007fAABzW], tags={I7, PSI0}),backend=numpy, dtype=complex128, data=array([1.-0.j, 0.-0.j])
Tensor(shape=(2), inds=[_9c007fAABzX], tags={I8, PSI0}),backend=numpy, dtype=complex128, data=array([1.-0.j, 0.-0.j])
Tensor(shape=(2), inds=[_9c007fAABzY], tags={I9, PSI0}),backend=numpy, dtype=complex128, data=array([1.-0.j, 0.-0.j])
Tensor(shape=(2, 2), inds=[_9c007fAABzZ, _9c007fAABzP], tags={GATE_0, ROUND_0, H, I0}),backend=numpy, dtype=complex128, data=array([[ 0.70710678-0.j, 0.70710678-0.j],\n", " [ 0.70710678-0.j, -0.70710678-0.j]])
Tensor(shape=(2, 2), inds=[_9c007fAABza, _9c007fAABzQ], tags={GATE_1, ROUND_0, H, I1}),backend=numpy, dtype=complex128, data=array([[ 0.70710678-0.j, 0.70710678-0.j],\n", " [ 0.70710678-0.j, -0.70710678-0.j]])
Tensor(shape=(2, 2), inds=[_9c007fAABzb, _9c007fAABzR], tags={GATE_2, ROUND_0, H, I2}),backend=numpy, dtype=complex128, data=array([[ 0.70710678-0.j, 0.70710678-0.j],\n", " [ 0.70710678-0.j, -0.70710678-0.j]])
Tensor(shape=(2, 2), inds=[_9c007fAABzc, _9c007fAABzS], tags={GATE_3, ROUND_0, H, I3}),backend=numpy, dtype=complex128, data=array([[ 0.70710678-0.j, 0.70710678-0.j],\n", " [ 0.70710678-0.j, -0.70710678-0.j]])
Tensor(shape=(2, 2), inds=[_9c007fAABze, _9c007fAABzT], tags={GATE_4, ROUND_0, H, I4}),backend=numpy, dtype=complex128, data=array([[ 0.70710678-0.j, 0.70710678-0.j],\n", " [ 0.70710678-0.j, -0.70710678-0.j]])
Tensor(shape=(2, 2), inds=[_9c007fAABzf, _9c007fAABzU], tags={GATE_5, ROUND_0, H, I5}),backend=numpy, dtype=complex128, data=array([[ 0.70710678-0.j, 0.70710678-0.j],\n", " [ 0.70710678-0.j, -0.70710678-0.j]])
Tensor(shape=(2, 2), inds=[_9c007fAABzh, _9c007fAABzV], tags={GATE_6, ROUND_0, H, I6}),backend=numpy, dtype=complex128, data=array([[ 0.70710678-0.j, 0.70710678-0.j],\n", " [ 0.70710678-0.j, -0.70710678-0.j]])
Tensor(shape=(2, 2), inds=[_9c007fAABzi, _9c007fAABzW], tags={GATE_7, ROUND_0, H, I7}),backend=numpy, dtype=complex128, data=array([[ 0.70710678-0.j, 0.70710678-0.j],\n", " [ 0.70710678-0.j, -0.70710678-0.j]])
Tensor(shape=(2, 2), inds=[_9c007fAABzk, _9c007fAABzX], tags={GATE_8, ROUND_0, H, I8}),backend=numpy, dtype=complex128, data=array([[ 0.70710678-0.j, 0.70710678-0.j],\n", " [ 0.70710678-0.j, -0.70710678-0.j]])
Tensor(shape=(2, 2), inds=[_9c007fAABzl, _9c007fAABzY], tags={GATE_9, ROUND_0, H, I9}),backend=numpy, dtype=complex128, data=array([[ 0.70710678-0.j, 0.70710678-0.j],\n", " [ 0.70710678-0.j, -0.70710678-0.j]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAABzn, _9c007fAABzZ, b], tags={GATE_10, ROUND_1, CX, I0}),backend=numpy, dtype=complex128, data=array([[[ 1.18920712-0.j, 0. -0.j],\n", " [ 0. -0.j, 0. -0.j]],\n", "\n", " [[ 0. -0.j, 0. -0.j],\n", " [ 0. -0.j, -1.18920712-0.j]]])
Tensor(shape=(2, 2, 2), inds=[b, _9c007fAABzo, _9c007fAABza], tags={GATE_10, ROUND_1, CX, I1}),backend=numpy, dtype=complex128, data=array([[[ 0.84089642-0.j, 0. -0.j],\n", " [ 0. -0.j, 0.84089642-0.j]],\n", "\n", " [[-0. -0.j, -0.84089642-0.j],\n", " [-0.84089642-0.j, 0. -0.j]]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAABzp, _9c007fAABzb, _9c007fAABzd], tags={GATE_11, ROUND_1, CX, I2}),backend=numpy, dtype=complex128, data=array([[[ 1.18920712-0.j, 0. -0.j],\n", " [ 0. -0.j, 0. -0.j]],\n", "\n", " [[ 0. -0.j, 0. -0.j],\n", " [ 0. -0.j, -1.18920712-0.j]]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAABzd, _9c007fAABzq, _9c007fAABzc], tags={GATE_11, ROUND_1, CX, I3}),backend=numpy, dtype=complex128, data=array([[[ 0.84089642-0.j, 0. -0.j],\n", " [ 0. -0.j, 0.84089642-0.j]],\n", "\n", " [[-0. -0.j, -0.84089642-0.j],\n", " [-0.84089642-0.j, 0. -0.j]]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAABzr, _9c007fAABze, _9c007fAABzg], tags={GATE_12, ROUND_1, CX, I4}),backend=numpy, dtype=complex128, data=array([[[ 1.18920712-0.j, 0. -0.j],\n", " [ 0. -0.j, 0. -0.j]],\n", "\n", " [[ 0. -0.j, 0. -0.j],\n", " [ 0. -0.j, -1.18920712-0.j]]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAABzg, _9c007fAABzs, _9c007fAABzf], tags={GATE_12, ROUND_1, CX, I5}),backend=numpy, dtype=complex128, data=array([[[ 0.84089642-0.j, 0. -0.j],\n", " [ 0. -0.j, 0.84089642-0.j]],\n", "\n", " [[-0. -0.j, -0.84089642-0.j],\n", " [-0.84089642-0.j, 0. -0.j]]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAABzt, _9c007fAABzh, _9c007fAABzj], tags={GATE_13, ROUND_1, CX, I6}),backend=numpy, dtype=complex128, data=array([[[ 1.18920712-0.j, 0. -0.j],\n", " [ 0. -0.j, 0. -0.j]],\n", "\n", " [[ 0. -0.j, 0. -0.j],\n", " [ 0. -0.j, -1.18920712-0.j]]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAABzj, _9c007fAABzu, _9c007fAABzi], tags={GATE_13, ROUND_1, CX, I7}),backend=numpy, dtype=complex128, data=array([[[ 0.84089642-0.j, 0. -0.j],\n", " [ 0. -0.j, 0.84089642-0.j]],\n", "\n", " [[-0. -0.j, -0.84089642-0.j],\n", " [-0.84089642-0.j, 0. -0.j]]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAABzv, _9c007fAABzk, _9c007fAABzm], tags={GATE_14, ROUND_1, CX, I8}),backend=numpy, dtype=complex128, data=array([[[ 1.18920712-0.j, 0. -0.j],\n", " [ 0. -0.j, 0. -0.j]],\n", "\n", " [[ 0. -0.j, 0. -0.j],\n", " [ 0. -0.j, -1.18920712-0.j]]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAABzm, _9c007fAABzw, _9c007fAABzl], tags={GATE_14, ROUND_1, CX, I9}),backend=numpy, dtype=complex128, data=array([[[ 0.84089642-0.j, 0. -0.j],\n", " [ 0. -0.j, 0.84089642-0.j]],\n", "\n", " [[-0. -0.j, -0.84089642-0.j],\n", " [-0.84089642-0.j, 0. -0.j]]])
Tensor(shape=(2, 2), inds=[_9c007fAACAJ, _9c007fAABzn], tags={GATE_15, ROUND_1, RZ, I0}),backend=numpy, dtype=complex128, data=array([[0.8156179+0.57859091j, 0. -0.j ],\n", " [0. -0.j , 0.8156179-0.57859091j]])
Tensor(shape=(2, 2), inds=[_9c007fAABzx, _9c007fAABzo], tags={GATE_16, ROUND_1, RZ, I1}),backend=numpy, dtype=complex128, data=array([[0.8156179+0.57859091j, 0. -0.j ],\n", " [0. -0.j , 0.8156179-0.57859091j]])
Tensor(shape=(2, 2), inds=[_9c007fAABzy, _9c007fAABzp], tags={GATE_17, ROUND_1, RZ, I2}),backend=numpy, dtype=complex128, data=array([[0.8156179+0.57859091j, 0. -0.j ],\n", " [0. -0.j , 0.8156179-0.57859091j]])
Tensor(shape=(2, 2), inds=[_9c007fAACAA, _9c007fAABzq], tags={GATE_18, ROUND_1, RZ, I3}),backend=numpy, dtype=complex128, data=array([[0.8156179+0.57859091j, 0. -0.j ],\n", " [0. -0.j , 0.8156179-0.57859091j]])
Tensor(shape=(2, 2), inds=[_9c007fAACAB, _9c007fAABzr], tags={GATE_19, ROUND_1, RZ, I4}),backend=numpy, dtype=complex128, data=array([[0.8156179+0.57859091j, 0. -0.j ],\n", " [0. -0.j , 0.8156179-0.57859091j]])
Tensor(shape=(2, 2), inds=[_9c007fAACAD, _9c007fAABzs], tags={GATE_20, ROUND_1, RZ, I5}),backend=numpy, dtype=complex128, data=array([[0.8156179+0.57859091j, 0. -0.j ],\n", " [0. -0.j , 0.8156179-0.57859091j]])
Tensor(shape=(2, 2), inds=[_9c007fAACAE, _9c007fAABzt], tags={GATE_21, ROUND_1, RZ, I6}),backend=numpy, dtype=complex128, data=array([[0.8156179+0.57859091j, 0. -0.j ],\n", " [0. -0.j , 0.8156179-0.57859091j]])
Tensor(shape=(2, 2), inds=[_9c007fAACAG, _9c007fAABzu], tags={GATE_22, ROUND_1, RZ, I7}),backend=numpy, dtype=complex128, data=array([[0.8156179+0.57859091j, 0. -0.j ],\n", " [0. -0.j , 0.8156179-0.57859091j]])
Tensor(shape=(2, 2), inds=[_9c007fAACAH, _9c007fAABzv], tags={GATE_23, ROUND_1, RZ, I8}),backend=numpy, dtype=complex128, data=array([[0.8156179+0.57859091j, 0. -0.j ],\n", " [0. -0.j , 0.8156179-0.57859091j]])
Tensor(shape=(2, 2), inds=[_9c007fAACAS, _9c007fAABzw], tags={GATE_24, ROUND_1, RZ, I9}),backend=numpy, dtype=complex128, data=array([[0.8156179+0.57859091j, 0. -0.j ],\n", " [0. -0.j , 0.8156179-0.57859091j]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAACAK, _9c007fAABzx, _9c007fAABzz], tags={GATE_25, ROUND_1, CZ, I1}),backend=numpy, dtype=complex128, data=array([[[-0.34461337-0.j, 1.13818065-0.j],\n", " [ 0. -0.j, 0. -0.j]],\n", "\n", " [[ 0. -0.j, 0. -0.j],\n", " [-1.13818065-0.j, -0.34461337-0.j]]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAABzz, _9c007fAACAL, _9c007fAABzy], tags={GATE_25, ROUND_1, CZ, I2}),backend=numpy, dtype=complex128, data=array([[[-1.04849371-0.j, 0. -0.j],\n", " [ 0. -0.j, 0.5611368 -0.j]],\n", "\n", " [[ 0.5611368 -0.j, 0. -0.j],\n", " [ 0. -0.j, 1.04849371-0.j]]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAACAM, _9c007fAACAA, _9c007fAACAC], tags={GATE_26, ROUND_1, CZ, I3}),backend=numpy, dtype=complex128, data=array([[[-0.34461337-0.j, 1.13818065-0.j],\n", " [ 0. -0.j, 0. -0.j]],\n", "\n", " [[ 0. -0.j, 0. -0.j],\n", " [-1.13818065-0.j, -0.34461337-0.j]]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAACAC, _9c007fAACAN, _9c007fAACAB], tags={GATE_26, ROUND_1, CZ, I4}),backend=numpy, dtype=complex128, data=array([[[-1.04849371-0.j, 0. -0.j],\n", " [ 0. -0.j, 0.5611368 -0.j]],\n", "\n", " [[ 0.5611368 -0.j, 0. -0.j],\n", " [ 0. -0.j, 1.04849371-0.j]]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAACAO, _9c007fAACAD, _9c007fAACAF], tags={GATE_27, ROUND_1, CZ, I5}),backend=numpy, dtype=complex128, data=array([[[-0.34461337-0.j, 1.13818065-0.j],\n", " [ 0. -0.j, 0. -0.j]],\n", "\n", " [[ 0. -0.j, 0. -0.j],\n", " [-1.13818065-0.j, -0.34461337-0.j]]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAACAF, _9c007fAACAP, _9c007fAACAE], tags={GATE_27, ROUND_1, CZ, I6}),backend=numpy, dtype=complex128, data=array([[[-1.04849371-0.j, 0. -0.j],\n", " [ 0. -0.j, 0.5611368 -0.j]],\n", "\n", " [[ 0.5611368 -0.j, 0. -0.j],\n", " [ 0. -0.j, 1.04849371-0.j]]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAACAQ, _9c007fAACAG, _9c007fAACAI], tags={GATE_28, ROUND_1, CZ, I7}),backend=numpy, dtype=complex128, data=array([[[-0.34461337-0.j, 1.13818065-0.j],\n", " [ 0. -0.j, 0. -0.j]],\n", "\n", " [[ 0. -0.j, 0. -0.j],\n", " [-1.13818065-0.j, -0.34461337-0.j]]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAACAI, _9c007fAACAR, _9c007fAACAH], tags={GATE_28, ROUND_1, CZ, I8}),backend=numpy, dtype=complex128, data=array([[[-1.04849371-0.j, 0. -0.j],\n", " [ 0. -0.j, 0.5611368 -0.j]],\n", "\n", " [[ 0.5611368 -0.j, 0. -0.j],\n", " [ 0. -0.j, 1.04849371-0.j]]])
Tensor(shape=(2, 2), inds=[_9c007fAACAT, _9c007fAACAJ], tags={GATE_29, ROUND_1, RX, I0}),backend=numpy, dtype=complex128, data=array([[0.8156179-0.j , 0. +0.57859091j],\n", " [0. +0.57859091j, 0.8156179-0.j ]])
Tensor(shape=(2, 2), inds=[_9c007fAACAU, _9c007fAACAK], tags={GATE_30, ROUND_1, RX, I1}),backend=numpy, dtype=complex128, data=array([[0.8156179-0.j , 0. +0.57859091j],\n", " [0. +0.57859091j, 0.8156179-0.j ]])
Tensor(shape=(2, 2), inds=[_9c007fAACAW, _9c007fAACAL], tags={GATE_31, ROUND_1, RX, I2}),backend=numpy, dtype=complex128, data=array([[0.8156179-0.j , 0. +0.57859091j],\n", " [0. +0.57859091j, 0.8156179-0.j ]])
Tensor(shape=(2, 2), inds=[_9c007fAACAX, _9c007fAACAM], tags={GATE_32, ROUND_1, RX, I3}),backend=numpy, dtype=complex128, data=array([[0.8156179-0.j , 0. +0.57859091j],\n", " [0. +0.57859091j, 0.8156179-0.j ]])
Tensor(shape=(2, 2), inds=[_9c007fAACAZ, _9c007fAACAN], tags={GATE_33, ROUND_1, RX, I4}),backend=numpy, dtype=complex128, data=array([[0.8156179-0.j , 0. +0.57859091j],\n", " [0. +0.57859091j, 0.8156179-0.j ]])
Tensor(shape=(2, 2), inds=[_9c007fAACAa, _9c007fAACAO], tags={GATE_34, ROUND_1, RX, I5}),backend=numpy, dtype=complex128, data=array([[0.8156179-0.j , 0. +0.57859091j],\n", " [0. +0.57859091j, 0.8156179-0.j ]])
Tensor(shape=(2, 2), inds=[_9c007fAACAc, _9c007fAACAP], tags={GATE_35, ROUND_1, RX, I6}),backend=numpy, dtype=complex128, data=array([[0.8156179-0.j , 0. +0.57859091j],\n", " [0. +0.57859091j, 0.8156179-0.j ]])
Tensor(shape=(2, 2), inds=[_9c007fAACAd, _9c007fAACAQ], tags={GATE_36, ROUND_1, RX, I7}),backend=numpy, dtype=complex128, data=array([[0.8156179-0.j , 0. +0.57859091j],\n", " [0. +0.57859091j, 0.8156179-0.j ]])
Tensor(shape=(2, 2), inds=[_9c007fAACAf, _9c007fAACAR], tags={GATE_37, ROUND_1, RX, I8}),backend=numpy, dtype=complex128, data=array([[0.8156179-0.j , 0. +0.57859091j],\n", " [0. +0.57859091j, 0.8156179-0.j ]])
Tensor(shape=(2, 2), inds=[_9c007fAACAg, _9c007fAACAS], tags={GATE_38, ROUND_1, RX, I9}),backend=numpy, dtype=complex128, data=array([[0.8156179-0.j , 0. +0.57859091j],\n", " [0. +0.57859091j, 0.8156179-0.j ]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAACAi, _9c007fAACAT, _9c007fAACAV], tags={GATE_39, ROUND_2, CX, I0}),backend=numpy, dtype=complex128, data=array([[[ 1.18920712-0.j, 0. -0.j],\n", " [ 0. -0.j, 0. -0.j]],\n", "\n", " [[ 0. -0.j, 0. -0.j],\n", " [ 0. -0.j, -1.18920712-0.j]]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAACAV, _9c007fAACAj, _9c007fAACAU], tags={GATE_39, ROUND_2, CX, I1}),backend=numpy, dtype=complex128, data=array([[[ 0.84089642-0.j, 0. -0.j],\n", " [ 0. -0.j, 0.84089642-0.j]],\n", "\n", " [[-0. -0.j, -0.84089642-0.j],\n", " [-0.84089642-0.j, 0. -0.j]]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAACAk, _9c007fAACAW, _9c007fAACAY], tags={GATE_40, ROUND_2, CX, I2}),backend=numpy, dtype=complex128, data=array([[[ 1.18920712-0.j, 0. -0.j],\n", " [ 0. -0.j, 0. -0.j]],\n", "\n", " [[ 0. -0.j, 0. -0.j],\n", " [ 0. -0.j, -1.18920712-0.j]]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAACAY, _9c007fAACAl, _9c007fAACAX], tags={GATE_40, ROUND_2, CX, I3}),backend=numpy, dtype=complex128, data=array([[[ 0.84089642-0.j, 0. -0.j],\n", " [ 0. -0.j, 0.84089642-0.j]],\n", "\n", " [[-0. -0.j, -0.84089642-0.j],\n", " [-0.84089642-0.j, 0. -0.j]]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAACAm, _9c007fAACAZ, _9c007fAACAb], tags={GATE_41, ROUND_2, CX, I4}),backend=numpy, dtype=complex128, data=array([[[ 1.18920712-0.j, 0. -0.j],\n", " [ 0. -0.j, 0. -0.j]],\n", "\n", " [[ 0. -0.j, 0. -0.j],\n", " [ 0. -0.j, -1.18920712-0.j]]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAACAb, _9c007fAACAn, _9c007fAACAa], tags={GATE_41, ROUND_2, CX, I5}),backend=numpy, dtype=complex128, data=array([[[ 0.84089642-0.j, 0. -0.j],\n", " [ 0. -0.j, 0.84089642-0.j]],\n", "\n", " [[-0. -0.j, -0.84089642-0.j],\n", " [-0.84089642-0.j, 0. -0.j]]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAACAo, _9c007fAACAc, _9c007fAACAe], tags={GATE_42, ROUND_2, CX, I6}),backend=numpy, dtype=complex128, data=array([[[ 1.18920712-0.j, 0. -0.j],\n", " [ 0. -0.j, 0. -0.j]],\n", "\n", " [[ 0. -0.j, 0. -0.j],\n", " [ 0. -0.j, -1.18920712-0.j]]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAACAe, _9c007fAACAp, _9c007fAACAd], tags={GATE_42, ROUND_2, CX, I7}),backend=numpy, dtype=complex128, data=array([[[ 0.84089642-0.j, 0. -0.j],\n", " [ 0. -0.j, 0.84089642-0.j]],\n", "\n", " [[-0. -0.j, -0.84089642-0.j],\n", " [-0.84089642-0.j, 0. -0.j]]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAACAq, _9c007fAACAf, _9c007fAACAh], tags={GATE_43, ROUND_2, CX, I8}),backend=numpy, dtype=complex128, data=array([[[ 1.18920712-0.j, 0. -0.j],\n", " [ 0. -0.j, 0. -0.j]],\n", "\n", " [[ 0. -0.j, 0. -0.j],\n", " [ 0. -0.j, -1.18920712-0.j]]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAACAh, _9c007fAACAr, _9c007fAACAg], tags={GATE_43, ROUND_2, CX, I9}),backend=numpy, dtype=complex128, data=array([[[ 0.84089642-0.j, 0. -0.j],\n", " [ 0. -0.j, 0.84089642-0.j]],\n", "\n", " [[-0. -0.j, -0.84089642-0.j],\n", " [-0.84089642-0.j, 0. -0.j]]])
Tensor(shape=(2, 2), inds=[_9c007fAACBE, _9c007fAACAi], tags={GATE_44, ROUND_2, RZ, I0}),backend=numpy, dtype=complex128, data=array([[0.8156179+0.57859091j, 0. -0.j ],\n", " [0. -0.j , 0.8156179-0.57859091j]])
Tensor(shape=(2, 2), inds=[_9c007fAACAs, _9c007fAACAj], tags={GATE_45, ROUND_2, RZ, I1}),backend=numpy, dtype=complex128, data=array([[0.8156179+0.57859091j, 0. -0.j ],\n", " [0. -0.j , 0.8156179-0.57859091j]])
Tensor(shape=(2, 2), inds=[_9c007fAACAt, _9c007fAACAk], tags={GATE_46, ROUND_2, RZ, I2}),backend=numpy, dtype=complex128, data=array([[0.8156179+0.57859091j, 0. -0.j ],\n", " [0. -0.j , 0.8156179-0.57859091j]])
Tensor(shape=(2, 2), inds=[_9c007fAACAv, _9c007fAACAl], tags={GATE_47, ROUND_2, RZ, I3}),backend=numpy, dtype=complex128, data=array([[0.8156179+0.57859091j, 0. -0.j ],\n", " [0. -0.j , 0.8156179-0.57859091j]])
Tensor(shape=(2, 2), inds=[_9c007fAACAw, _9c007fAACAm], tags={GATE_48, ROUND_2, RZ, I4}),backend=numpy, dtype=complex128, data=array([[0.8156179+0.57859091j, 0. -0.j ],\n", " [0. -0.j , 0.8156179-0.57859091j]])
Tensor(shape=(2, 2), inds=[_9c007fAACAy, _9c007fAACAn], tags={GATE_49, ROUND_2, RZ, I5}),backend=numpy, dtype=complex128, data=array([[0.8156179+0.57859091j, 0. -0.j ],\n", " [0. -0.j , 0.8156179-0.57859091j]])
Tensor(shape=(2, 2), inds=[_9c007fAACAz, _9c007fAACAo], tags={GATE_50, ROUND_2, RZ, I6}),backend=numpy, dtype=complex128, data=array([[0.8156179+0.57859091j, 0. -0.j ],\n", " [0. -0.j , 0.8156179-0.57859091j]])
Tensor(shape=(2, 2), inds=[_9c007fAACBB, _9c007fAACAp], tags={GATE_51, ROUND_2, RZ, I7}),backend=numpy, dtype=complex128, data=array([[0.8156179+0.57859091j, 0. -0.j ],\n", " [0. -0.j , 0.8156179-0.57859091j]])
Tensor(shape=(2, 2), inds=[_9c007fAACBC, _9c007fAACAq], tags={GATE_52, ROUND_2, RZ, I8}),backend=numpy, dtype=complex128, data=array([[0.8156179+0.57859091j, 0. -0.j ],\n", " [0. -0.j , 0.8156179-0.57859091j]])
Tensor(shape=(2, 2), inds=[_9c007fAACBN, _9c007fAACAr], tags={GATE_53, ROUND_2, RZ, I9}),backend=numpy, dtype=complex128, data=array([[0.8156179+0.57859091j, 0. -0.j ],\n", " [0. -0.j , 0.8156179-0.57859091j]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAACBF, _9c007fAACAs, _9c007fAACAu], tags={GATE_54, ROUND_2, CZ, I1}),backend=numpy, dtype=complex128, data=array([[[-0.34461337-0.j, 1.13818065-0.j],\n", " [ 0. -0.j, 0. -0.j]],\n", "\n", " [[ 0. -0.j, 0. -0.j],\n", " [-1.13818065-0.j, -0.34461337-0.j]]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAACAu, _9c007fAACBG, _9c007fAACAt], tags={GATE_54, ROUND_2, CZ, I2}),backend=numpy, dtype=complex128, data=array([[[-1.04849371-0.j, 0. -0.j],\n", " [ 0. -0.j, 0.5611368 -0.j]],\n", "\n", " [[ 0.5611368 -0.j, 0. -0.j],\n", " [ 0. -0.j, 1.04849371-0.j]]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAACBH, _9c007fAACAv, _9c007fAACAx], tags={GATE_55, ROUND_2, CZ, I3}),backend=numpy, dtype=complex128, data=array([[[-0.34461337-0.j, 1.13818065-0.j],\n", " [ 0. -0.j, 0. -0.j]],\n", "\n", " [[ 0. -0.j, 0. -0.j],\n", " [-1.13818065-0.j, -0.34461337-0.j]]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAACAx, _9c007fAACBI, _9c007fAACAw], tags={GATE_55, ROUND_2, CZ, I4}),backend=numpy, dtype=complex128, data=array([[[-1.04849371-0.j, 0. -0.j],\n", " [ 0. -0.j, 0.5611368 -0.j]],\n", "\n", " [[ 0.5611368 -0.j, 0. -0.j],\n", " [ 0. -0.j, 1.04849371-0.j]]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAACBJ, _9c007fAACAy, _9c007fAACBA], tags={GATE_56, ROUND_2, CZ, I5}),backend=numpy, dtype=complex128, data=array([[[-0.34461337-0.j, 1.13818065-0.j],\n", " [ 0. -0.j, 0. -0.j]],\n", "\n", " [[ 0. -0.j, 0. -0.j],\n", " [-1.13818065-0.j, -0.34461337-0.j]]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAACBA, _9c007fAACBK, _9c007fAACAz], tags={GATE_56, ROUND_2, CZ, I6}),backend=numpy, dtype=complex128, data=array([[[-1.04849371-0.j, 0. -0.j],\n", " [ 0. -0.j, 0.5611368 -0.j]],\n", "\n", " [[ 0.5611368 -0.j, 0. -0.j],\n", " [ 0. -0.j, 1.04849371-0.j]]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAACBL, _9c007fAACBB, _9c007fAACBD], tags={GATE_57, ROUND_2, CZ, I7}),backend=numpy, dtype=complex128, data=array([[[-0.34461337-0.j, 1.13818065-0.j],\n", " [ 0. -0.j, 0. -0.j]],\n", "\n", " [[ 0. -0.j, 0. -0.j],\n", " [-1.13818065-0.j, -0.34461337-0.j]]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAACBD, _9c007fAACBM, _9c007fAACBC], tags={GATE_57, ROUND_2, CZ, I8}),backend=numpy, dtype=complex128, data=array([[[-1.04849371-0.j, 0. -0.j],\n", " [ 0. -0.j, 0.5611368 -0.j]],\n", "\n", " [[ 0.5611368 -0.j, 0. -0.j],\n", " [ 0. -0.j, 1.04849371-0.j]]])
Tensor(shape=(2, 2), inds=[_9c007fAACBO, _9c007fAACBE], tags={GATE_58, ROUND_2, RX, I0}),backend=numpy, dtype=complex128, data=array([[0.8156179-0.j , 0. +0.57859091j],\n", " [0. +0.57859091j, 0.8156179-0.j ]])
Tensor(shape=(2, 2), inds=[_9c007fAACBP, _9c007fAACBF], tags={GATE_59, ROUND_2, RX, I1}),backend=numpy, dtype=complex128, data=array([[0.8156179-0.j , 0. +0.57859091j],\n", " [0. +0.57859091j, 0.8156179-0.j ]])
Tensor(shape=(2, 2), inds=[_9c007fAACBR, _9c007fAACBG], tags={GATE_60, ROUND_2, RX, I2}),backend=numpy, dtype=complex128, data=array([[0.8156179-0.j , 0. +0.57859091j],\n", " [0. +0.57859091j, 0.8156179-0.j ]])
Tensor(shape=(2, 2), inds=[_9c007fAACBS, _9c007fAACBH], tags={GATE_61, ROUND_2, RX, I3}),backend=numpy, dtype=complex128, data=array([[0.8156179-0.j , 0. +0.57859091j],\n", " [0. +0.57859091j, 0.8156179-0.j ]])
Tensor(shape=(2, 2), inds=[_9c007fAACBU, _9c007fAACBI], tags={GATE_62, ROUND_2, RX, I4}),backend=numpy, dtype=complex128, data=array([[0.8156179-0.j , 0. +0.57859091j],\n", " [0. +0.57859091j, 0.8156179-0.j ]])
Tensor(shape=(2, 2), inds=[_9c007fAACBV, _9c007fAACBJ], tags={GATE_63, ROUND_2, RX, I5}),backend=numpy, dtype=complex128, data=array([[0.8156179-0.j , 0. +0.57859091j],\n", " [0. +0.57859091j, 0.8156179-0.j ]])
Tensor(shape=(2, 2), inds=[_9c007fAACBX, _9c007fAACBK], tags={GATE_64, ROUND_2, RX, I6}),backend=numpy, dtype=complex128, data=array([[0.8156179-0.j , 0. +0.57859091j],\n", " [0. +0.57859091j, 0.8156179-0.j ]])
Tensor(shape=(2, 2), inds=[_9c007fAACBY, _9c007fAACBL], tags={GATE_65, ROUND_2, RX, I7}),backend=numpy, dtype=complex128, data=array([[0.8156179-0.j , 0. +0.57859091j],\n", " [0. +0.57859091j, 0.8156179-0.j ]])
Tensor(shape=(2, 2), inds=[_9c007fAACBa, _9c007fAACBM], tags={GATE_66, ROUND_2, RX, I8}),backend=numpy, dtype=complex128, data=array([[0.8156179-0.j , 0. +0.57859091j],\n", " [0. +0.57859091j, 0.8156179-0.j ]])
Tensor(shape=(2, 2), inds=[_9c007fAACBb, _9c007fAACBN], tags={GATE_67, ROUND_2, RX, I9}),backend=numpy, dtype=complex128, data=array([[0.8156179-0.j , 0. +0.57859091j],\n", " [0. +0.57859091j, 0.8156179-0.j ]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAACBd, _9c007fAACBO, _9c007fAACBQ], tags={GATE_68, ROUND_3, CX, I0}),backend=numpy, dtype=complex128, data=array([[[ 1.18920712-0.j, 0. -0.j],\n", " [ 0. -0.j, 0. -0.j]],\n", "\n", " [[ 0. -0.j, 0. -0.j],\n", " [ 0. -0.j, -1.18920712-0.j]]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAACBQ, _9c007fAACBe, _9c007fAACBP], tags={GATE_68, ROUND_3, CX, I1}),backend=numpy, dtype=complex128, data=array([[[ 0.84089642-0.j, 0. -0.j],\n", " [ 0. -0.j, 0.84089642-0.j]],\n", "\n", " [[-0. -0.j, -0.84089642-0.j],\n", " [-0.84089642-0.j, 0. -0.j]]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAACBf, _9c007fAACBR, _9c007fAACBT], tags={GATE_69, ROUND_3, CX, I2}),backend=numpy, dtype=complex128, data=array([[[ 1.18920712-0.j, 0. -0.j],\n", " [ 0. -0.j, 0. -0.j]],\n", "\n", " [[ 0. -0.j, 0. -0.j],\n", " [ 0. -0.j, -1.18920712-0.j]]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAACBT, _9c007fAACBg, _9c007fAACBS], tags={GATE_69, ROUND_3, CX, I3}),backend=numpy, dtype=complex128, data=array([[[ 0.84089642-0.j, 0. -0.j],\n", " [ 0. -0.j, 0.84089642-0.j]],\n", "\n", " [[-0. -0.j, -0.84089642-0.j],\n", " [-0.84089642-0.j, 0. -0.j]]])

...

" ], "text/plain": [ "TensorNetworkGen(tensors=668, indices=802)" ] }, "execution_count": 33, "metadata": {}, "output_type": "execute_result" } ], "source": [ "norm = circ.psi.H & circ.psi\n", "norm" ] }, { "cell_type": "code", "execution_count": 34, "id": "899cfb2b-f7f4-41bf-b4f0-a5cd3918b951", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
TensorNetworkGen(tensors=87, indices=67)
Tensor(shape=(2, 2), inds=[_9c007fAAFbM, _9c007fAAFbN], tags={GATE_138, ROUND_5, RZ, I7, GATE_144, CZ, I8}),backend=numpy, dtype=complex128, data=array([[ 0.8156179-0.57859091j, 0.8156179-0.57859091j],\n", " [ 0.8156179+0.57859091j, -0.8156179-0.57859091j]])
Tensor(shape=(2, 2), inds=[_9c007fAACDu, _9c007fAAFbL], tags={GATE_136, ROUND_5, RZ, I5, GATE_143, CZ, I6}),backend=numpy, dtype=complex128, data=array([[ 0.8156179-0.57859091j, 0.8156179-0.57859091j],\n", " [ 0.8156179+0.57859091j, -0.8156179-0.57859091j]])
Tensor(shape=(2, 2), inds=[_9c007fAAFaR, _9c007fAAFaS], tags={GATE_109, ROUND_4, RZ, I7, GATE_115, CZ, I8}),backend=numpy, dtype=complex128, data=array([[ 0.8156179-0.57859091j, 0.8156179-0.57859091j],\n", " [ 0.8156179+0.57859091j, -0.8156179-0.57859091j]])
Tensor(shape=(2, 2), inds=[_9c007fAAFaP, _9c007fAAFaQ], tags={GATE_107, ROUND_4, RZ, I5, GATE_114, CZ, I6}),backend=numpy, dtype=complex128, data=array([[ 0.8156179-0.57859091j, 0.8156179-0.57859091j],\n", " [ 0.8156179+0.57859091j, -0.8156179-0.57859091j]])
Tensor(shape=(2, 2), inds=[_9c007fAACCx, _9c007fAAFaO], tags={GATE_105, ROUND_4, RZ, I3, GATE_113, CZ, I4}),backend=numpy, dtype=complex128, data=array([[ 0.8156179-0.57859091j, 0.8156179-0.57859091j],\n", " [ 0.8156179+0.57859091j, -0.8156179-0.57859091j]])
Tensor(shape=(2, 2), inds=[_9c007fAAFZW, _9c007fAAFZX], tags={GATE_80, ROUND_3, RZ, I7, GATE_86, CZ, I8}),backend=numpy, dtype=complex128, data=array([[ 0.8156179-0.57859091j, 0.8156179-0.57859091j],\n", " [ 0.8156179+0.57859091j, -0.8156179-0.57859091j]])
Tensor(shape=(2, 2), inds=[_9c007fAAFZU, _9c007fAAFZV], tags={GATE_78, ROUND_3, RZ, I5, GATE_85, CZ, I6}),backend=numpy, dtype=complex128, data=array([[ 0.8156179-0.57859091j, 0.8156179-0.57859091j],\n", " [ 0.8156179+0.57859091j, -0.8156179-0.57859091j]])
Tensor(shape=(2, 2), inds=[_9c007fAAFZS, _9c007fAAFZT], tags={GATE_76, ROUND_3, RZ, I3, GATE_84, CZ, I4}),backend=numpy, dtype=complex128, data=array([[ 0.8156179-0.57859091j, 0.8156179-0.57859091j],\n", " [ 0.8156179+0.57859091j, -0.8156179-0.57859091j]])
Tensor(shape=(2, 2), inds=[_9c007fAACCA, _9c007fAAFZR], tags={GATE_74, ROUND_3, RZ, I1, GATE_83, CZ, I2}),backend=numpy, dtype=complex128, data=array([[ 0.8156179-0.57859091j, 0.8156179-0.57859091j],\n", " [ 0.8156179+0.57859091j, -0.8156179-0.57859091j]])
Tensor(shape=(2, 2), inds=[_9c007fAAFcH, _9c007fAACEs], tags={GATE_167, ROUND_6, RZ, I7, GATE_173, CZ, I8}),backend=numpy, dtype=complex128, data=array([[ 0.8156179+0.57859091j, 0.8156179+0.57859091j],\n", " [ 0.8156179-0.57859091j, -0.8156179+0.57859091j]])
Tensor(shape=(2, 2), inds=[_9c007fAACDw, _9c007fAACDx], tags={GATE_138, ROUND_5, RZ, I7, GATE_144, CZ, I8}),backend=numpy, dtype=complex128, data=array([[ 0.8156179+0.57859091j, 0.8156179+0.57859091j],\n", " [ 0.8156179-0.57859091j, -0.8156179+0.57859091j]])
Tensor(shape=(2, 2), inds=[_9c007fAACDB, _9c007fAACDC], tags={GATE_109, ROUND_4, RZ, I7, GATE_115, CZ, I8}),backend=numpy, dtype=complex128, data=array([[ 0.8156179+0.57859091j, 0.8156179+0.57859091j],\n", " [ 0.8156179-0.57859091j, -0.8156179+0.57859091j]])
Tensor(shape=(2, 2), inds=[_9c007fAACCz, _9c007fAACDA], tags={GATE_107, ROUND_4, RZ, I5, GATE_114, CZ, I6}),backend=numpy, dtype=complex128, data=array([[ 0.8156179+0.57859091j, 0.8156179+0.57859091j],\n", " [ 0.8156179-0.57859091j, -0.8156179+0.57859091j]])
Tensor(shape=(2, 2), inds=[_9c007fAACCG, _9c007fAACCH], tags={GATE_80, ROUND_3, RZ, I7, GATE_86, CZ, I8}),backend=numpy, dtype=complex128, data=array([[ 0.8156179+0.57859091j, 0.8156179+0.57859091j],\n", " [ 0.8156179-0.57859091j, -0.8156179+0.57859091j]])
Tensor(shape=(2, 2), inds=[_9c007fAACCE, _9c007fAACCF], tags={GATE_78, ROUND_3, RZ, I5, GATE_85, CZ, I6}),backend=numpy, dtype=complex128, data=array([[ 0.8156179+0.57859091j, 0.8156179+0.57859091j],\n", " [ 0.8156179-0.57859091j, -0.8156179+0.57859091j]])
Tensor(shape=(2, 2), inds=[_9c007fAACCC, _9c007fAACCD], tags={GATE_76, ROUND_3, RZ, I3, GATE_84, CZ, I4}),backend=numpy, dtype=complex128, data=array([[ 0.8156179+0.57859091j, 0.8156179+0.57859091j],\n", " [ 0.8156179-0.57859091j, -0.8156179+0.57859091j]])
Tensor(shape=(2, 2), inds=[_9c007fAAFcI, _9c007fAAFbN], tags={GATE_159, ROUND_6, CX, I8, GATE_153, ROUND_5, RX}),backend=numpy, dtype=complex128, data=array([[ 0.96993861+0.j , 0. -0.68806443j],\n", " [ 0. +0.68806443j, -0.96993861+0.j ]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAAFcI, _9c007fAAFcJ, _9c007fAAFbO], tags={GATE_168, ROUND_6, RZ, I8, GATE_169, I9, GATE_154, ROUND_5, RX, GATE_159, CX}),backend=numpy, dtype=complex128, data=array([[[ 2.26649549e-01-6.47317875e-01j,\n", " -4.59200617e-01-1.60782850e-01j],\n", " [-2.51780799e-18-4.86535026e-01j,\n", " 6.85850166e-01+2.33613923e-17j]],\n", "\n", " [[-2.51780799e-18+4.86535026e-01j,\n", " -6.85850166e-01+2.33613923e-17j],\n", " [-2.26649549e-01-6.47317875e-01j,\n", " -4.59200617e-01+1.60782850e-01j]]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAACEq, _9c007fAAFbM, _9c007fAAFcH], tags={GATE_166, ROUND_6, RZ, I6, GATE_152, ROUND_5, RX, I7, GATE_158, CX}),backend=numpy, dtype=complex128, data=array([[[ 0.55939167-0.39682667j, -0.28150475-0.39682667j],\n", " [-0.28150475-0.39682667j, 0.55939167-0.39682667j]],\n", "\n", " [[-0.28150475+0.39682667j, -0.55939167-0.39682667j],\n", " [-0.55939167-0.39682667j, -0.28150475+0.39682667j]]])
Tensor(shape=(2, 2), inds=[_9c007fAAFbN, _9c007fAAFaS], tags={GATE_130, ROUND_5, CX, I8, GATE_124, ROUND_4, RX}),backend=numpy, dtype=complex128, data=array([[ 0.96993861+0.j , 0. -0.68806443j],\n", " [ 0. +0.68806443j, -0.96993861+0.j ]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAAFbN, _9c007fAAFbO, _9c007fAAFaT], tags={GATE_139, ROUND_5, RZ, I8, GATE_140, I9, GATE_125, ROUND_4, RX, GATE_130, CX}),backend=numpy, dtype=complex128, data=array([[[ 2.26649549e-01-6.47317875e-01j,\n", " -4.59200617e-01-1.60782850e-01j],\n", " [-2.51780799e-18-4.86535026e-01j,\n", " 6.85850166e-01+2.33613923e-17j]],\n", "\n", " [[-2.51780799e-18+4.86535026e-01j,\n", " -6.85850166e-01+2.33613923e-17j],\n", " [-2.26649549e-01-6.47317875e-01j,\n", " -4.59200617e-01+1.60782850e-01j]]])
Tensor(shape=(2, 2), inds=[_9c007fAAFbL, _9c007fAAFaQ], tags={GATE_129, ROUND_5, CX, I6, GATE_122, ROUND_4, RX}),backend=numpy, dtype=complex128, data=array([[ 0.96993861+0.j , 0. -0.68806443j],\n", " [ 0. +0.68806443j, -0.96993861+0.j ]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAAFbL, _9c007fAAFaR, _9c007fAAFbM], tags={GATE_137, ROUND_5, RZ, I6, GATE_123, ROUND_4, RX, I7, GATE_129, CX}),backend=numpy, dtype=complex128, data=array([[[ 0.55939167-0.39682667j, -0.28150475-0.39682667j],\n", " [-0.28150475-0.39682667j, 0.55939167-0.39682667j]],\n", "\n", " [[-0.28150475+0.39682667j, -0.55939167-0.39682667j],\n", " [-0.55939167-0.39682667j, -0.28150475+0.39682667j]]])
Tensor(shape=(2, 2), inds=[_9c007fAAFaS, _9c007fAAFZX], tags={GATE_101, ROUND_4, CX, I8, GATE_95, ROUND_3, RX}),backend=numpy, dtype=complex128, data=array([[ 0.96993861+0.j , 0. -0.68806443j],\n", " [ 0. +0.68806443j, -0.96993861+0.j ]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAAFaS, _9c007fAAFaT, _9c007fAAFZY], tags={GATE_110, ROUND_4, RZ, I8, GATE_111, I9, GATE_96, ROUND_3, RX, GATE_101, CX}),backend=numpy, dtype=complex128, data=array([[[ 2.26649549e-01-6.47317875e-01j,\n", " -4.59200617e-01-1.60782850e-01j],\n", " [-2.51780799e-18-4.86535026e-01j,\n", " 6.85850166e-01+2.33613923e-17j]],\n", "\n", " [[-2.51780799e-18+4.86535026e-01j,\n", " -6.85850166e-01+2.33613923e-17j],\n", " [-2.26649549e-01-6.47317875e-01j,\n", " -4.59200617e-01+1.60782850e-01j]]])
Tensor(shape=(2, 2), inds=[_9c007fAAFaQ, _9c007fAAFZV], tags={GATE_100, ROUND_4, CX, I6, GATE_93, ROUND_3, RX}),backend=numpy, dtype=complex128, data=array([[ 0.96993861+0.j , 0. -0.68806443j],\n", " [ 0. +0.68806443j, -0.96993861+0.j ]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAAFaQ, _9c007fAAFZW, _9c007fAAFaR], tags={GATE_108, ROUND_4, RZ, I6, GATE_94, ROUND_3, RX, I7, GATE_100, CX}),backend=numpy, dtype=complex128, data=array([[[ 0.55939167-0.39682667j, -0.28150475-0.39682667j],\n", " [-0.28150475-0.39682667j, 0.55939167-0.39682667j]],\n", "\n", " [[-0.28150475+0.39682667j, -0.55939167-0.39682667j],\n", " [-0.55939167-0.39682667j, -0.28150475+0.39682667j]]])
Tensor(shape=(2, 2), inds=[_9c007fAAFaO, _9c007fAAFZT], tags={GATE_99, ROUND_4, CX, I4, GATE_91, ROUND_3, RX}),backend=numpy, dtype=complex128, data=array([[ 0.96993861+0.j , 0. -0.68806443j],\n", " [ 0. +0.68806443j, -0.96993861+0.j ]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAAFaO, _9c007fAAFZU, _9c007fAAFaP], tags={GATE_106, ROUND_4, RZ, I4, GATE_92, ROUND_3, RX, I5, GATE_99, CX}),backend=numpy, dtype=complex128, data=array([[[ 0.55939167-0.39682667j, -0.28150475-0.39682667j],\n", " [-0.28150475-0.39682667j, 0.55939167-0.39682667j]],\n", "\n", " [[-0.28150475+0.39682667j, -0.55939167-0.39682667j],\n", " [-0.55939167-0.39682667j, -0.28150475+0.39682667j]]])
Tensor(shape=(2, 2), inds=[_9c007fAAFZX, _9c007fAAFYc], tags={GATE_72, ROUND_3, CX, I8, GATE_66, ROUND_2, RX}),backend=numpy, dtype=complex128, data=array([[ 0.96993861+0.j , 0. -0.68806443j],\n", " [ 0. +0.68806443j, -0.96993861+0.j ]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAAFZX, _9c007fAAFZY, _9c007fAAFYd], tags={GATE_81, ROUND_3, RZ, I8, GATE_82, I9, GATE_67, ROUND_2, RX, GATE_72, CX}),backend=numpy, dtype=complex128, data=array([[[ 2.26649549e-01-6.47317875e-01j,\n", " -4.59200617e-01-1.60782850e-01j],\n", " [-2.51780799e-18-4.86535026e-01j,\n", " 6.85850166e-01+2.33613923e-17j]],\n", "\n", " [[-2.51780799e-18+4.86535026e-01j,\n", " -6.85850166e-01+2.33613923e-17j],\n", " [-2.26649549e-01-6.47317875e-01j,\n", " -4.59200617e-01+1.60782850e-01j]]])
Tensor(shape=(2, 2), inds=[_9c007fAAFZV, _9c007fAAFYa], tags={GATE_71, ROUND_3, CX, I6, GATE_64, ROUND_2, RX}),backend=numpy, dtype=complex128, data=array([[ 0.96993861+0.j , 0. -0.68806443j],\n", " [ 0. +0.68806443j, -0.96993861+0.j ]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAAFZV, _9c007fAAFYb, _9c007fAAFZW], tags={GATE_79, ROUND_3, RZ, I6, GATE_65, ROUND_2, RX, I7, GATE_71, CX}),backend=numpy, dtype=complex128, data=array([[[ 0.55939167-0.39682667j, -0.28150475-0.39682667j],\n", " [-0.28150475-0.39682667j, 0.55939167-0.39682667j]],\n", "\n", " [[-0.28150475+0.39682667j, -0.55939167-0.39682667j],\n", " [-0.55939167-0.39682667j, -0.28150475+0.39682667j]]])
Tensor(shape=(2, 2), inds=[_9c007fAAFZT, _9c007fAAFYY], tags={GATE_70, ROUND_3, CX, I4, GATE_62, ROUND_2, RX}),backend=numpy, dtype=complex128, data=array([[ 0.96993861+0.j , 0. -0.68806443j],\n", " [ 0. +0.68806443j, -0.96993861+0.j ]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAAFZT, _9c007fAAFYZ, _9c007fAAFZU], tags={GATE_77, ROUND_3, RZ, I4, GATE_63, ROUND_2, RX, I5, GATE_70, CX}),backend=numpy, dtype=complex128, data=array([[[ 0.55939167-0.39682667j, -0.28150475-0.39682667j],\n", " [-0.28150475-0.39682667j, 0.55939167-0.39682667j]],\n", "\n", " [[-0.28150475+0.39682667j, -0.55939167-0.39682667j],\n", " [-0.55939167-0.39682667j, -0.28150475+0.39682667j]]])
Tensor(shape=(2, 2), inds=[_9c007fAAFZR, _9c007fAAFYW], tags={GATE_69, ROUND_3, CX, I2, GATE_60, ROUND_2, RX}),backend=numpy, dtype=complex128, data=array([[ 0.96993861+0.j , 0. -0.68806443j],\n", " [ 0. +0.68806443j, -0.96993861+0.j ]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAAFZR, _9c007fAAFYX, _9c007fAAFZS], tags={GATE_75, ROUND_3, RZ, I2, GATE_61, ROUND_2, RX, I3, GATE_69, CX}),backend=numpy, dtype=complex128, data=array([[[ 0.55939167-0.39682667j, -0.28150475-0.39682667j],\n", " [-0.28150475-0.39682667j, 0.55939167-0.39682667j]],\n", "\n", " [[-0.28150475+0.39682667j, -0.55939167-0.39682667j],\n", " [-0.55939167-0.39682667j, -0.28150475+0.39682667j]]])
Tensor(shape=(2, 2), inds=[_9c007fAAFYc, _9c007fAAFYb], tags={GATE_43, ROUND_2, CX, I8, GATE_51, RZ, I7, GATE_57, CZ}),backend=numpy, dtype=complex128, data=array([[ 0.96993861-0.68806443j, 0.96993861+0.68806443j],\n", " [-0.96993861+0.68806443j, 0.96993861+0.68806443j]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAAFYa, _9c007fAAFXg, _9c007fAAFYb], tags={GATE_50, ROUND_2, RZ, I6, GATE_36, ROUND_1, RX, I7, GATE_42, CX}),backend=numpy, dtype=complex128, data=array([[[ 0.55939167-0.39682667j, -0.28150475-0.39682667j],\n", " [-0.28150475-0.39682667j, 0.55939167-0.39682667j]],\n", "\n", " [[-0.28150475+0.39682667j, -0.55939167-0.39682667j],\n", " [-0.55939167-0.39682667j, -0.28150475+0.39682667j]]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAACFn, _9c007fAAFdE, _9c007fAACEt], tags={GATE_197, ROUND_7, RZ, I8, GATE_198, I9, GATE_183, ROUND_6, RX, GATE_188, CX}),backend=numpy, dtype=complex128, data=array([[[ 2.26649549e-01+6.47317875e-01j,\n", " -4.59200617e-01+1.60782850e-01j],\n", " [-2.51780799e-18+4.86535026e-01j,\n", " 6.85850166e-01-2.33613923e-17j]],\n", "\n", " [[-2.51780799e-18-4.86535026e-01j,\n", " -6.85850166e-01-2.33613923e-17j],\n", " [-2.26649549e-01+6.47317875e-01j,\n", " -4.59200617e-01-1.60782850e-01j]]])
Tensor(shape=(2, 2), inds=[_9c007fAACEs, _9c007fAACDx], tags={GATE_159, ROUND_6, CX, I8, GATE_153, ROUND_5, RX}),backend=numpy, dtype=complex128, data=array([[ 0.96993861-0.j , 0. +0.68806443j],\n", " [ 0. -0.68806443j, -0.96993861+0.j ]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAACEs, _9c007fAACEt, _9c007fAACDy], tags={GATE_168, ROUND_6, RZ, I8, GATE_169, I9, GATE_154, ROUND_5, RX, GATE_159, CX}),backend=numpy, dtype=complex128, data=array([[[ 2.26649549e-01+6.47317875e-01j,\n", " -4.59200617e-01+1.60782850e-01j],\n", " [-2.51780799e-18+4.86535026e-01j,\n", " 6.85850166e-01-2.33613923e-17j]],\n", "\n", " [[-2.51780799e-18-4.86535026e-01j,\n", " -6.85850166e-01-2.33613923e-17j],\n", " [-2.26649549e-01+6.47317875e-01j,\n", " -4.59200617e-01-1.60782850e-01j]]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAACEq, _9c007fAACDw, _9c007fAAFcH], tags={GATE_166, ROUND_6, RZ, I6, GATE_152, ROUND_5, RX, I7, GATE_158, CX}),backend=numpy, dtype=complex128, data=array([[[ 0.55939167+0.39682667j, -0.28150475+0.39682667j],\n", " [-0.28150475+0.39682667j, 0.55939167+0.39682667j]],\n", "\n", " [[-0.28150475-0.39682667j, -0.55939167+0.39682667j],\n", " [-0.55939167+0.39682667j, -0.28150475-0.39682667j]]])
Tensor(shape=(2, 2), inds=[_9c007fAACDx, _9c007fAACDC], tags={GATE_130, ROUND_5, CX, I8, GATE_124, ROUND_4, RX}),backend=numpy, dtype=complex128, data=array([[ 0.96993861-0.j , 0. +0.68806443j],\n", " [ 0. -0.68806443j, -0.96993861+0.j ]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAACDx, _9c007fAACDy, _9c007fAACDD], tags={GATE_139, ROUND_5, RZ, I8, GATE_140, I9, GATE_125, ROUND_4, RX, GATE_130, CX}),backend=numpy, dtype=complex128, data=array([[[ 2.26649549e-01+6.47317875e-01j,\n", " -4.59200617e-01+1.60782850e-01j],\n", " [-2.51780799e-18+4.86535026e-01j,\n", " 6.85850166e-01-2.33613923e-17j]],\n", "\n", " [[-2.51780799e-18-4.86535026e-01j,\n", " -6.85850166e-01-2.33613923e-17j],\n", " [-2.26649549e-01+6.47317875e-01j,\n", " -4.59200617e-01-1.60782850e-01j]]])
Tensor(shape=(2, 2), inds=[_9c007fAACDv, _9c007fAACDA], tags={GATE_129, ROUND_5, CX, I6, GATE_122, ROUND_4, RX}),backend=numpy, dtype=complex128, data=array([[ 0.96993861-0.j , 0. +0.68806443j],\n", " [ 0. -0.68806443j, -0.96993861+0.j ]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAACDv, _9c007fAACDB, _9c007fAACDw], tags={GATE_137, ROUND_5, RZ, I6, GATE_123, ROUND_4, RX, I7, GATE_129, CX}),backend=numpy, dtype=complex128, data=array([[[ 0.55939167+0.39682667j, -0.28150475+0.39682667j],\n", " [-0.28150475+0.39682667j, 0.55939167+0.39682667j]],\n", "\n", " [[-0.28150475-0.39682667j, -0.55939167+0.39682667j],\n", " [-0.55939167+0.39682667j, -0.28150475-0.39682667j]]])
Tensor(shape=(2, 2), inds=[_9c007fAAFbJ, _9c007fAACCy], tags={GATE_128, ROUND_5, CX, I4, GATE_120, ROUND_4, RX}),backend=numpy, dtype=complex128, data=array([[ 0.96993861-0.j , 0. +0.68806443j],\n", " [ 0. -0.68806443j, -0.96993861+0.j ]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAAFbJ, _9c007fAACCz, _9c007fAACDu], tags={GATE_135, ROUND_5, RZ, I4, GATE_121, ROUND_4, RX, I5, GATE_128, CX}),backend=numpy, dtype=complex128, data=array([[[ 0.55939167+0.39682667j, -0.28150475+0.39682667j],\n", " [-0.28150475+0.39682667j, 0.55939167+0.39682667j]],\n", "\n", " [[-0.28150475-0.39682667j, -0.55939167+0.39682667j],\n", " [-0.55939167+0.39682667j, -0.28150475-0.39682667j]]])
Tensor(shape=(2, 2), inds=[_9c007fAACDC, _9c007fAACCH], tags={GATE_101, ROUND_4, CX, I8, GATE_95, ROUND_3, RX}),backend=numpy, dtype=complex128, data=array([[ 0.96993861-0.j , 0. +0.68806443j],\n", " [ 0. -0.68806443j, -0.96993861+0.j ]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAACDC, _9c007fAACDD, _9c007fAACCI], tags={GATE_110, ROUND_4, RZ, I8, GATE_111, I9, GATE_96, ROUND_3, RX, GATE_101, CX}),backend=numpy, dtype=complex128, data=array([[[ 2.26649549e-01+6.47317875e-01j,\n", " -4.59200617e-01+1.60782850e-01j],\n", " [-2.51780799e-18+4.86535026e-01j,\n", " 6.85850166e-01-2.33613923e-17j]],\n", "\n", " [[-2.51780799e-18-4.86535026e-01j,\n", " -6.85850166e-01-2.33613923e-17j],\n", " [-2.26649549e-01+6.47317875e-01j,\n", " -4.59200617e-01-1.60782850e-01j]]])
Tensor(shape=(2, 2), inds=[_9c007fAACDA, _9c007fAACCF], tags={GATE_100, ROUND_4, CX, I6, GATE_93, ROUND_3, RX}),backend=numpy, dtype=complex128, data=array([[ 0.96993861-0.j , 0. +0.68806443j],\n", " [ 0. -0.68806443j, -0.96993861+0.j ]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAACDA, _9c007fAACCG, _9c007fAACDB], tags={GATE_108, ROUND_4, RZ, I6, GATE_94, ROUND_3, RX, I7, GATE_100, CX}),backend=numpy, dtype=complex128, data=array([[[ 0.55939167+0.39682667j, -0.28150475+0.39682667j],\n", " [-0.28150475+0.39682667j, 0.55939167+0.39682667j]],\n", "\n", " [[-0.28150475-0.39682667j, -0.55939167+0.39682667j],\n", " [-0.55939167+0.39682667j, -0.28150475-0.39682667j]]])
Tensor(shape=(2, 2), inds=[_9c007fAACCy, _9c007fAACCD], tags={GATE_99, ROUND_4, CX, I4, GATE_91, ROUND_3, RX}),backend=numpy, dtype=complex128, data=array([[ 0.96993861-0.j , 0. +0.68806443j],\n", " [ 0. -0.68806443j, -0.96993861+0.j ]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAACCy, _9c007fAACCE, _9c007fAACCz], tags={GATE_106, ROUND_4, RZ, I4, GATE_92, ROUND_3, RX, I5, GATE_99, CX}),backend=numpy, dtype=complex128, data=array([[[ 0.55939167+0.39682667j, -0.28150475+0.39682667j],\n", " [-0.28150475+0.39682667j, 0.55939167+0.39682667j]],\n", "\n", " [[-0.28150475-0.39682667j, -0.55939167+0.39682667j],\n", " [-0.55939167+0.39682667j, -0.28150475-0.39682667j]]])
Tensor(shape=(2, 2), inds=[_9c007fAAFaM, _9c007fAACCB], tags={GATE_98, ROUND_4, CX, I2, GATE_89, ROUND_3, RX}),backend=numpy, dtype=complex128, data=array([[ 0.96993861-0.j , 0. +0.68806443j],\n", " [ 0. -0.68806443j, -0.96993861+0.j ]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAAFaM, _9c007fAACCC, _9c007fAACCx], tags={GATE_104, ROUND_4, RZ, I2, GATE_90, ROUND_3, RX, I3, GATE_98, CX}),backend=numpy, dtype=complex128, data=array([[[ 0.55939167+0.39682667j, -0.28150475+0.39682667j],\n", " [-0.28150475+0.39682667j, 0.55939167+0.39682667j]],\n", "\n", " [[-0.28150475-0.39682667j, -0.55939167+0.39682667j],\n", " [-0.55939167+0.39682667j, -0.28150475-0.39682667j]]])
Tensor(shape=(2, 2), inds=[_9c007fAACCH, _9c007fAACBM], tags={GATE_72, ROUND_3, CX, I8, GATE_66, ROUND_2, RX}),backend=numpy, dtype=complex128, data=array([[ 0.96993861-0.j , 0. +0.68806443j],\n", " [ 0. -0.68806443j, -0.96993861+0.j ]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAACCH, _9c007fAACCI, _9c007fAACBN], tags={GATE_81, ROUND_3, RZ, I8, GATE_82, I9, GATE_67, ROUND_2, RX, GATE_72, CX}),backend=numpy, dtype=complex128, data=array([[[ 2.26649549e-01+6.47317875e-01j,\n", " -4.59200617e-01+1.60782850e-01j],\n", " [-2.51780799e-18+4.86535026e-01j,\n", " 6.85850166e-01-2.33613923e-17j]],\n", "\n", " [[-2.51780799e-18-4.86535026e-01j,\n", " -6.85850166e-01-2.33613923e-17j],\n", " [-2.26649549e-01+6.47317875e-01j,\n", " -4.59200617e-01-1.60782850e-01j]]])
Tensor(shape=(2, 2), inds=[_9c007fAACCF, _9c007fAACBK], tags={GATE_71, ROUND_3, CX, I6, GATE_64, ROUND_2, RX}),backend=numpy, dtype=complex128, data=array([[ 0.96993861-0.j , 0. +0.68806443j],\n", " [ 0. -0.68806443j, -0.96993861+0.j ]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAACCF, _9c007fAACBL, _9c007fAACCG], tags={GATE_79, ROUND_3, RZ, I6, GATE_65, ROUND_2, RX, I7, GATE_71, CX}),backend=numpy, dtype=complex128, data=array([[[ 0.55939167+0.39682667j, -0.28150475+0.39682667j],\n", " [-0.28150475+0.39682667j, 0.55939167+0.39682667j]],\n", "\n", " [[-0.28150475-0.39682667j, -0.55939167+0.39682667j],\n", " [-0.55939167+0.39682667j, -0.28150475-0.39682667j]]])
Tensor(shape=(2, 2), inds=[_9c007fAACCD, _9c007fAACBI], tags={GATE_70, ROUND_3, CX, I4, GATE_62, ROUND_2, RX}),backend=numpy, dtype=complex128, data=array([[ 0.96993861-0.j , 0. +0.68806443j],\n", " [ 0. -0.68806443j, -0.96993861+0.j ]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAACCD, _9c007fAACBJ, _9c007fAACCE], tags={GATE_77, ROUND_3, RZ, I4, GATE_63, ROUND_2, RX, I5, GATE_70, CX}),backend=numpy, dtype=complex128, data=array([[[ 0.55939167+0.39682667j, -0.28150475+0.39682667j],\n", " [-0.28150475+0.39682667j, 0.55939167+0.39682667j]],\n", "\n", " [[-0.28150475-0.39682667j, -0.55939167+0.39682667j],\n", " [-0.55939167+0.39682667j, -0.28150475-0.39682667j]]])
Tensor(shape=(2, 2), inds=[_9c007fAACCB, _9c007fAACBG], tags={GATE_69, ROUND_3, CX, I2, GATE_60, ROUND_2, RX}),backend=numpy, dtype=complex128, data=array([[ 0.96993861-0.j , 0. +0.68806443j],\n", " [ 0. -0.68806443j, -0.96993861+0.j ]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAACCB, _9c007fAACBH, _9c007fAACCC], tags={GATE_75, ROUND_3, RZ, I2, GATE_61, ROUND_2, RX, I3, GATE_69, CX}),backend=numpy, dtype=complex128, data=array([[[ 0.55939167+0.39682667j, -0.28150475+0.39682667j],\n", " [-0.28150475+0.39682667j, 0.55939167+0.39682667j]],\n", "\n", " [[-0.28150475-0.39682667j, -0.55939167+0.39682667j],\n", " [-0.55939167+0.39682667j, -0.28150475-0.39682667j]]])
Tensor(shape=(2, 2), inds=[_9c007fAACBM, _9c007fAACBL], tags={GATE_43, ROUND_2, CX, I8, GATE_51, RZ, I7, GATE_57, CZ}),backend=numpy, dtype=complex128, data=array([[ 0.96993861+0.68806443j, 0.96993861-0.68806443j],\n", " [-0.96993861-0.68806443j, 0.96993861-0.68806443j]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAACBK, _9c007fAACAQ, _9c007fAACBL], tags={GATE_50, ROUND_2, RZ, I6, GATE_36, ROUND_1, RX, I7, GATE_42, CX}),backend=numpy, dtype=complex128, data=array([[[ 0.55939167+0.39682667j, -0.28150475+0.39682667j],\n", " [-0.28150475+0.39682667j, 0.55939167+0.39682667j]],\n", "\n", " [[-0.28150475-0.39682667j, -0.55939167+0.39682667j],\n", " [-0.55939167+0.39682667j, -0.28150475-0.39682667j]]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAACBM, _9c007fAACAQ, _9c007fAACBN], tags={GATE_52, ROUND_2, RZ, I8, GATE_14, ROUND_1, CX, GATE_22, I7, GATE_28, CZ, GATE_37, RX, GATE_23, GATE_8, ROUND_0, H, GATE_53, I9, GATE_38, GATE_43, GATE_24, GATE_9, GATE_21, I6, GATE_6, GATE_7, GATE_13}),backend=numpy, dtype=complex128, data=array([[[ 0.5027836 +0.11370736j, -0.02428325-0.12148106j],\n", " [-0.02428325-0.12148106j, -0.02428325+0.01722628j]],\n", "\n", " [[ 0.02428325+0.01722628j, 0.1226808 +0.01722628j],\n", " [-0.02428325+0.12148106j, 0.142557 +0.49537684j]]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAAFYc, _9c007fAAFXg, _9c007fAAFYd], tags={GATE_52, ROUND_2, RZ, I8, GATE_14, ROUND_1, CX, GATE_22, I7, GATE_28, CZ, GATE_37, RX, GATE_23, GATE_8, ROUND_0, H, GATE_53, I9, GATE_38, GATE_43, GATE_24, GATE_9, GATE_21, I6, GATE_6, GATE_7, GATE_13}),backend=numpy, dtype=complex128, data=array([[[ 0.5027836 -0.11370736j, -0.02428325+0.12148106j],\n", " [-0.02428325+0.12148106j, -0.02428325-0.01722628j]],\n", "\n", " [[ 0.02428325-0.01722628j, 0.1226808 -0.01722628j],\n", " [-0.02428325-0.12148106j, 0.142557 -0.49537684j]]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAACBH, _9c007fAACBI, _9c007fAACBG], tags={GATE_47, ROUND_2, RZ, I3, GATE_55, CZ, I4, GATE_41, CX, GATE_33, ROUND_1, RX, GATE_12, GATE_18, GATE_26, GATE_19, GATE_4, ROUND_0, H, GATE_5, I5, GATE_46, I2, GATE_32, GATE_40, GATE_17, GATE_2, GATE_3, GATE_11}),backend=numpy, dtype=complex128, data=array([[[-0.53798078+3.63434853e-01j, 0.28016385-2.68095775e-17j],\n", " [ 0.09258438+2.64423745e-01j, -0.16523255+6.27858598e-01j]],\n", "\n", " [[-0.28016385+7.81422312e-17j, -0.64718797-5.15358662e-02j],\n", " [-0.16523255+6.27858598e-01j, 0.09258438-2.64423745e-01j]]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAACBJ, _9c007fAACBK, _9c007fAACBI], tags={GATE_49, ROUND_2, RZ, I5, GATE_56, CZ, I6, GATE_42, CX, GATE_35, ROUND_1, RX, GATE_13, GATE_20, GATE_27, GATE_21, GATE_6, ROUND_0, H, GATE_7, I7, GATE_48, I4, GATE_34, GATE_41, GATE_19, GATE_4, GATE_5, GATE_12}),backend=numpy, dtype=complex128, data=array([[[-0.53798078+3.63434853e-01j, 0.28016385-2.68095775e-17j],\n", " [ 0.09258438+2.64423745e-01j, -0.16523255+6.27858598e-01j]],\n", "\n", " [[-0.28016385+7.81422312e-17j, -0.64718797-5.15358662e-02j],\n", " [-0.16523255+6.27858598e-01j, 0.09258438-2.64423745e-01j]]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAAFYX, _9c007fAAFYY, _9c007fAAFYW], tags={GATE_47, ROUND_2, RZ, I3, GATE_55, CZ, I4, GATE_41, CX, GATE_33, ROUND_1, RX, GATE_12, GATE_18, GATE_26, GATE_19, GATE_4, ROUND_0, H, GATE_5, I5, GATE_46, I2, GATE_32, GATE_40, GATE_17, GATE_2, GATE_3, GATE_11}),backend=numpy, dtype=complex128, data=array([[[-0.53798078-3.63434853e-01j, 0.28016385+2.68095775e-17j],\n", " [ 0.09258438-2.64423745e-01j, -0.16523255-6.27858598e-01j]],\n", "\n", " [[-0.28016385-7.81422312e-17j, -0.64718797+5.15358662e-02j],\n", " [-0.16523255-6.27858598e-01j, 0.09258438+2.64423745e-01j]]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAAFYZ, _9c007fAAFYa, _9c007fAAFYY], tags={GATE_49, ROUND_2, RZ, I5, GATE_56, CZ, I6, GATE_42, CX, GATE_35, ROUND_1, RX, GATE_13, GATE_20, GATE_27, GATE_21, GATE_6, ROUND_0, H, GATE_7, I7, GATE_48, I4, GATE_34, GATE_41, GATE_19, GATE_4, GATE_5, GATE_12}),backend=numpy, dtype=complex128, data=array([[[-0.53798078-3.63434853e-01j, 0.28016385+2.68095775e-17j],\n", " [ 0.09258438-2.64423745e-01j, -0.16523255-6.27858598e-01j]],\n", "\n", " [[-0.28016385-7.81422312e-17j, -0.64718797+5.15358662e-02j],\n", " [-0.16523255-6.27858598e-01j, 0.09258438+2.64423745e-01j]]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAACFn, _9c007fAAFdE, _9c007fAAFcJ], tags={GATE_197, ROUND_7, RZ, I8, GATE_198, I9, GATE_183, ROUND_6, RX, GATE_188, CX, GATE_212, GATE_217, ROUND_8, GATE_227, GATE_241, GATE_251, ROUND_9, H}),backend=numpy, dtype=complex128, data=array([[[-1.90588793e-01+5.44327281e-01j,\n", " 3.86140152e-01+1.35201722e-01j],\n", " [ 2.11721571e-18+4.09125559e-01j,\n", " -5.76728946e-01-1.96445111e-17j]],\n", "\n", " [[ 2.11721571e-18-4.09125559e-01j,\n", " 5.76728946e-01-1.96445111e-17j],\n", " [ 1.90588793e-01+5.44327281e-01j,\n", " 3.86140152e-01-1.35201722e-01j]]])
Tensor(shape=(2, 2), inds=[_9c007fAACFn, _9c007fAACEs], tags={GATE_188, ROUND_7, CX, I8, GATE_182, ROUND_6, RX, GATE_217, ROUND_8, GATE_211, GATE_212, I9, GATE_227, RZ, GATE_241, GATE_251, ROUND_9, H, GATE_226, GATE_240, GATE_250, GATE_231, CZ, I7, GATE_225}),backend=numpy, dtype=complex128, data=array([[ 0.9407809 -0.66738026j, 0.47343266+0.66738026j],\n", " [-0.47343266-0.66738026j, -0.9407809 +0.66738026j]])
Tensor(shape=(2, 2), inds=[_9c007fAACFn, _9c007fAAFcI], tags={GATE_188, ROUND_7, CX, I8, GATE_182, ROUND_6, RX, GATE_196, RZ, I7, GATE_202, CZ}),backend=numpy, dtype=complex128, data=array([[-9.69938606e-01+0.00000000e+00j, 0.00000000e+00+6.88064432e-01j],\n", " [-5.07964202e-18-6.88064432e-01j, 9.69938606e-01-7.16058072e-18j]])
Tensor(shape=(2, 2), inds=[_9c007fAAFcH, _9c007fAAFcI], tags={GATE_167, ROUND_6, RZ, I7, GATE_173, CZ, I8, GATE_195, ROUND_7, I6, GATE_181, RX, GATE_187, CX, GATE_210, GATE_216, ROUND_8, GATE_224, GATE_238, GATE_248, ROUND_9, H, GATE_225, GATE_239, GATE_249, GATE_226, GATE_231, GATE_196, GATE_202}),backend=numpy, dtype=complex128, data=array([[-0.62892738+0.44615459j, -0.62892738+0.44615459j],\n", " [-0.62892738-0.44615459j, 0.62892738+0.44615459j]])
Tensor(shape=(2, 2), inds=[_9c007fAACEq, _9c007fAACDv], tags={GATE_158, ROUND_6, CX, I6, GATE_151, ROUND_5, RX, GATE_187, ROUND_7, GATE_180, GATE_195, RZ, GATE_181, I7, GATE_210, GATE_216, ROUND_8, GATE_224, GATE_238, GATE_248, ROUND_9, H, GATE_225, GATE_239, GATE_249, GATE_226, I8, GATE_231, CZ, GATE_196, GATE_202, GATE_209, GATE_230, I5, GATE_223, GATE_194, GATE_201, GATE_208, GATE_215, GATE_222, I4, GATE_236, GATE_246, GATE_237, GATE_247}),backend=numpy, dtype=complex128, data=array([[ 1.15345789e+00-5.31640927e-17j, 3.77140584e-17+8.18251118e-01j],\n", " [-4.66651456e-17-8.18251118e-01j, -1.15345789e+00+6.57821044e-17j]])
Tensor(shape=(2, 2), inds=[_9c007fAACEq, _9c007fAAFbL], tags={GATE_158, ROUND_6, CX, I6, GATE_151, ROUND_5, RX, GATE_165, RZ, I5, GATE_172, CZ}),backend=numpy, dtype=complex128, data=array([[-9.69938606e-01+0.00000000e+00j, 0.00000000e+00+6.88064432e-01j],\n", " [ 5.07964202e-18-6.88064432e-01j, 9.69938606e-01+7.16058072e-18j]])
Tensor(shape=(2, 2), inds=[_9c007fAACDu, _9c007fAACDv], tags={GATE_136, ROUND_5, RZ, I5, GATE_143, CZ, I6, GATE_164, ROUND_6, I4, GATE_150, RX, GATE_157, CX, GATE_193, ROUND_7, GATE_179, GATE_186, GATE_194, GATE_201, GATE_208, GATE_215, ROUND_8, GATE_222, GATE_236, GATE_246, ROUND_9, H, GATE_223, GATE_237, GATE_247, GATE_224, GATE_230, GATE_165, GATE_172}),backend=numpy, dtype=complex128, data=array([[ 0.61545076+0.43659441j, 0.61545076+0.43659441j],\n", " [ 0.61545076-0.43659441j, -0.61545076+0.43659441j]])
Tensor(shape=(2, 2), inds=[_9c007fAAFbJ, _9c007fAAFaO], tags={GATE_128, ROUND_5, CX, I4, GATE_120, ROUND_4, RX, GATE_157, ROUND_6, GATE_149, GATE_164, RZ, GATE_150, I5, GATE_193, ROUND_7, GATE_179, GATE_186, GATE_194, GATE_201, CZ, I6, GATE_208, GATE_215, ROUND_8, GATE_222, GATE_236, GATE_246, ROUND_9, H, GATE_223, GATE_237, GATE_247, GATE_224, GATE_230, GATE_165, GATE_172, GATE_178, GATE_192, I3, GATE_200, GATE_206, GATE_214, GATE_220, I2, GATE_234, GATE_244, GATE_221, GATE_235, GATE_245, GATE_229, GATE_207, GATE_163, GATE_171, GATE_191, GATE_177, GATE_185}),backend=numpy, dtype=complex128, data=array([[ 1.15345789e+00-6.10152182e-17j, -4.32835657e-17-8.18251118e-01j],\n", " [ 5.45687982e-17+8.18251118e-01j, -1.15345789e+00+7.69235868e-17j]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAAFbJ, _9c007fAAFaP, _9c007fAACDu], tags={GATE_135, ROUND_5, RZ, I4, GATE_121, ROUND_4, RX, I5, GATE_128, CX, GATE_134, I3, GATE_142, CZ}),backend=numpy, dtype=complex128, data=array([[[-0.55939167+0.39682667j, 0.28150475+0.39682667j],\n", " [ 0.28150475+0.39682667j, -0.55939167+0.39682667j]],\n", "\n", " [[ 0.28150475-0.39682667j, 0.55939167+0.39682667j],\n", " [ 0.55939167+0.39682667j, 0.28150475-0.39682667j]]])
Tensor(shape=(2, 2), inds=[_9c007fAACCx, _9c007fAACCy], tags={GATE_105, ROUND_4, RZ, I3, GATE_113, CZ, I4, GATE_133, ROUND_5, I2, GATE_119, RX, GATE_127, CX, GATE_162, ROUND_6, GATE_148, GATE_156, GATE_163, GATE_171, GATE_191, ROUND_7, GATE_177, GATE_185, GATE_192, GATE_200, GATE_206, GATE_214, ROUND_8, GATE_220, GATE_234, GATE_244, ROUND_9, H, GATE_221, GATE_235, GATE_245, GATE_222, GATE_229, GATE_134, GATE_142}),backend=numpy, dtype=complex128, data=array([[ 0.61212697+0.43423655j, 0.61212697+0.43423655j],\n", " [ 0.61212697-0.43423655j, -0.61212697+0.43423655j]])
Tensor(shape=(2, 2), inds=[_9c007fAAFaM, _9c007fAAFZR], tags={GATE_98, ROUND_4, CX, I2, GATE_89, ROUND_3, RX, GATE_127, ROUND_5, GATE_118, GATE_133, RZ, GATE_119, I3, GATE_162, ROUND_6, GATE_148, GATE_156, GATE_163, GATE_171, CZ, I4, GATE_191, ROUND_7, GATE_177, GATE_185, GATE_192, GATE_200, GATE_206, GATE_214, ROUND_8, GATE_220, GATE_234, GATE_244, ROUND_9, H, GATE_221, GATE_235, GATE_245, GATE_222, GATE_229, GATE_134, GATE_142, GATE_132, I1, GATE_141, GATE_160, I0, GATE_146, GATE_155, GATE_161, GATE_170, GATE_189, GATE_175, GATE_184, GATE_190, GATE_199, GATE_219, GATE_233, GATE_243, GATE_204, GATE_213, GATE_218, GATE_228, GATE_147, GATE_176, GATE_205}),backend=numpy, dtype=complex128, data=array([[ 1.02947075e+00+1.00729510e-17j, 7.14564743e-18-7.30295919e-01j],\n", " [-1.41994610e-17+7.30295919e-01j, -1.02947075e+00-2.00164473e-17j]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAAFaM, _9c007fAAFZS, _9c007fAACCx], tags={GATE_104, ROUND_4, RZ, I2, GATE_90, ROUND_3, RX, I3, GATE_98, CX, GATE_103, I1, GATE_112, CZ}),backend=numpy, dtype=complex128, data=array([[[-0.55939167+0.39682667j, 0.28150475+0.39682667j],\n", " [ 0.28150475+0.39682667j, -0.55939167+0.39682667j]],\n", "\n", " [[ 0.28150475-0.39682667j, 0.55939167+0.39682667j],\n", " [ 0.55939167+0.39682667j, 0.28150475-0.39682667j]]])
Tensor(shape=(2, 2, 2), inds=[_9c007fAACCA, _9c007fAAFYW, _9c007fAACBG], tags={GATE_97, ROUND_4, CX, I0, GATE_87, ROUND_3, RX, GATE_73, RZ, GATE_59, ROUND_2, I1, GATE_68, GATE_45, GATE_54, CZ, I2, GATE_40, GATE_31, ROUND_1, GATE_11, GATE_39, GATE_16, GATE_25, GATE_30, GATE_10, GATE_0, ROUND_0, H, GATE_1, GATE_15, GATE_29, GATE_44, GATE_58, GATE_17, GATE_2, GATE_3, I3, GATE_126, ROUND_5, GATE_116, GATE_155, ROUND_6, GATE_145, GATE_184, ROUND_7, GATE_174, GATE_213, ROUND_8, GATE_203, GATE_218, GATE_232, GATE_242, ROUND_9, GATE_219, GATE_233, GATE_243, GATE_204, GATE_220, GATE_228, GATE_189, GATE_175, GATE_190, GATE_199, GATE_160, GATE_146, GATE_161, GATE_170, GATE_131, GATE_117, GATE_132, GATE_141, GATE_156, GATE_147, GATE_185, GATE_176, GATE_214, GATE_205, GATE_206, GATE_234, GATE_244, GATE_221, GATE_235, GATE_245, GATE_222, I4, GATE_229, GATE_191, GATE_177, GATE_192, GATE_200, GATE_162, GATE_148, GATE_163, GATE_171, GATE_102, GATE_88, GATE_103, GATE_112}),backend=numpy, dtype=complex128, data=array([[[ 0.9367917 -0.66455036j, -0.01811946-0.44375959j],\n", " [ 0.41284054+0.16374854j, 0.30657993-0.21748464j]],\n", "\n", " [[ 0.4201327 -0.2980378j , -0.3613847 -0.56550071j],\n", " [ 0.41430484+0.52795972j, 1.05034446-0.74510352j]]])
Tensor(shape=(2, 2), inds=[_9c007fAACCA, _9c007fAACCB], tags={GATE_74, ROUND_3, RZ, I1, GATE_83, CZ, I2, GATE_102, ROUND_4, I0, GATE_88, RX, GATE_97, CX, GATE_131, ROUND_5, GATE_117, GATE_126, GATE_132, GATE_141, GATE_160, ROUND_6, GATE_146, GATE_155, GATE_161, GATE_170, GATE_189, ROUND_7, GATE_175, GATE_184, GATE_190, GATE_199, GATE_219, ROUND_8, GATE_233, GATE_243, ROUND_9, H, GATE_204, GATE_213, GATE_218, GATE_220, GATE_228, GATE_156, GATE_147, GATE_185, GATE_176, GATE_214, GATE_205, GATE_206, I3, GATE_234, GATE_244, GATE_221, GATE_235, GATE_245, GATE_222, I4, GATE_229, GATE_191, GATE_177, GATE_192, GATE_200, GATE_162, GATE_148, GATE_163, GATE_171, GATE_103, GATE_112}),backend=numpy, dtype=complex128, data=array([[-0.21325027+0.74103163j, -0.21325027+0.74103163j],\n", " [ 0.62892738+0.44615459j, -0.62892738-0.44615459j]])
" ], "text/plain": [ "TensorNetworkGen(tensors=87, indices=67)" ] }, "execution_count": 34, "metadata": {}, "output_type": "execute_result" } ], "source": [ "norm.full_simplify_(seq=\"ADCRS\")" ] }, { "cell_type": "markdown", "id": "e157ea5c-602a-4350-a4fc-5d90017ce765", "metadata": { "raw_mimetype": "text/restructuredtext" }, "source": [ "Here, [`TensorNetwork.full_simplify`](quimb.tensor.tensor_core.TensorNetwork.full_simplify), (which is the method that cycles through\n", "the other five) has reduced the 500 tensors to a single scalar via local simplifications only. Clearly we\n", "know the answer should be 1 anyway, but its nice to confirm it can indeed be found automatically as well:" ] }, { "cell_type": "code", "execution_count": 35, "id": "8e171bd1-0679-4879-9f75-6223c3bff6ce", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.99999999999996" ] }, "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# we specify output_inds since we now have a hyper tensor network\n", "norm.contract(..., output_inds=())" ] }, { "cell_type": "markdown", "id": "9fed741d-8ce0-48f3-8e6d-a5528fb55fb8", "metadata": { "raw_mimetype": "text/restructuredtext" }, "source": [ "## Finding a Contraction Path (the `optimize` kwarg)\n", "\n", "As mentioned previously, the main computational bottleneck (i.e. as we scale up circuits, the step that always\n", "becomes most expensive) is the actual *contraction* of the main tensor network objects, post simplification.\n", "The cost of this step (which is recorded in the rehearsal's `'tree'` objects) can be **incredibly sensitive**\n", "to the *contraction path* - the series of pairwise merges that define how to turn the collection of tensors into\n", "a single tensor.\n", "\n", "You control this aspect of the quantum circuit simulation via the `optimize` kwarg, which can take a number\n", "different types of values:\n", "\n", "> 1. A string, specifiying a [cotengra](https://cotengra.readthedocs.io/) registered path optimizer.\n", "> 2. A custom [`cotengra.HyperOptimizer`](cotengra.HyperOptimizer) instance\n", "> 2. A custom `opt_einsum.PathOptimizer` instance\n", "> 3. An explicit path - a sequence of pairs of ints - likely found from a previous rehearsal, for example.\n", "\n", ":::{note}\n", "The default value is `'auto-hq'` which is a high quality preset `cotengra` has, but this is\n", "**quite unlikely to offer the best performance for large or complex circuits**.\n", ":::\n", "\n", "As an example we'll show how to use each type of `optimize` kwarg for computing the local expecation:\n", "\n", "$$\n", "\\langle \\psi_{3, 4} | Z_3 \\otimes Z_4 | \\psi_{3, 4} \\rangle\n", "$$" ] }, { "cell_type": "code", "execution_count": 36, "id": "c4a292f2-14b4-40bf-be1f-81b057d529a6", "metadata": {}, "outputs": [], "source": [ "# compute the ZZ correlation on qubits 3 & 4\n", "ZZ = qu.pauli(\"Z\") & qu.pauli(\"Z\")\n", "where = (3, 4)" ] }, { "cell_type": "markdown", "id": "5433605f-5aa0-42c9-9fd6-d2b3b9ee1683", "metadata": { "raw_mimetype": "text/restructuredtext" }, "source": [ "### A `cotengra` preset\n", "\n", "First we use the fast but low quality `'greedy'` preset:" ] }, { "cell_type": "code", "execution_count": 37, "id": "794bf5cf-299a-4aa8-b97c-f7efdf83dd7a", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "222176" ] }, "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ "rehs = circ.local_expectation_rehearse(ZZ, where, optimize=\"greedy\")\n", "tn, tree = rehs[\"tn\"], rehs[\"tree\"]\n", "tree.contraction_cost()" ] }, { "cell_type": "markdown", "id": "48cc5518-5289-4921-81b3-11fa46e2e248", "metadata": { "raw_mimetype": "text/restructuredtext" }, "source": [ "Because we used a preset, the path is cached by `quimb`, meaning the\n", "path optimization won't run again for the same geometry.\n", "\n", "Now we can run the actual computation, reusing that path automatically:" ] }, { "cell_type": "code", "execution_count": 38, "id": "bc8642ae-7627-4090-a4fb-ba556025d9fe", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "84.4 ms ± 22 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n" ] } ], "source": [ "%%timeit\n", "circ.local_expectation(ZZ, where, optimize=\"greedy\")" ] }, { "cell_type": "markdown", "id": "53caeed7-76eb-46dd-bc03-2542a38e7885", "metadata": {}, "source": [ "We can compare this to just performing the main contraction:" ] }, { "cell_type": "code", "execution_count": 39, "id": "2ea2c78c-4bd0-4676-be55-70b32f8d10b2", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "5.44 ms ± 347 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" ] } ], "source": [ "%%timeit\n", "tn.contract(all, optimize=tree, output_inds=())" ] }, { "cell_type": "markdown", "id": "38526510-c40e-4f20-9430-3aefa6725365", "metadata": { "raw_mimetype": "text/restructuredtext" }, "source": [ "Where we see that most of the time is evidently spent preparing the TN.\n", "\n", "### An `opt_einsum.PathOptimizer` instance\n", "\n", "You can also supply a customized `PathOptimizer` instance here, an example of which\n", "is the `opt_einsum.RandomGreedy`\n", "[optimizer](https://optimized-einsum.readthedocs.io/en/stable/random_greedy_path.html)\n", "(which is itself called by `'auto-hq'` in fact)." ] }, { "cell_type": "code", "execution_count": 40, "id": "c355a3f1-062e-454d-be58-d8299ae9d846", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "222176" ] }, "execution_count": 40, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import opt_einsum as oe\n", "\n", "# up the number of repeats and make it run in parallel\n", "opt_rg = oe.RandomGreedy(max_repeats=256, parallel=True)\n", "\n", "rehs = circ.local_expectation_rehearse(ZZ, where, optimize=tree)\n", "tn, tree = rehs[\"tn\"], rehs[\"tree\"]\n", "tree.contraction_cost()" ] }, { "cell_type": "markdown", "id": "04f721cb-9afe-4a62-b668-410b43044cbe", "metadata": {}, "source": [ "We see it has found a much better path than `'greedy'`, which is not so surprising.\n", "\n", "Unlike before, if we want to reuse the path found from this, we can directly access the `.path`\n", "attribute from the `info` object (or the `PathOptimizer` object):" ] }, { "cell_type": "code", "execution_count": 41, "id": "b490e220-012d-45a7-875e-80ef1a793c36", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.04035324286372622" ] }, "execution_count": 41, "metadata": {}, "output_type": "execute_result" } ], "source": [ "tn.contract(all, output_inds=(), optimize=tree)" ] }, { "cell_type": "code", "execution_count": 42, "id": "cb659f49-7d2b-4bdb-89ba-aa10453298a8", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "69.4 ms ± 8.6 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n" ] } ], "source": [ "%%timeit\n", "circ.local_expectation(ZZ, where, optimize=tree)" ] }, { "cell_type": "markdown", "id": "0793283d-6a95-4310-a247-a3f749b68ce2", "metadata": { "raw_mimetype": "text/restructuredtext" }, "source": [ "We've shaved some time off but not much because the computation is not dominated by the\n", "contraction at this scale.\n", "\n", ":::{note}\n", "If you supplied the `opt_rg` optimizer again, it would run for an additional\n", "256 repeats, before returning its best path -- this can be useful if you want\n", "to incrementally optimize the path, check its cost and then optimize more,\n", "before switching to `.path` when you actually want to contract, for example.\n", ":::\n", "\n", "### A custom `opt_einsum.PathOptimizer` instance\n", "\n", "`opt_einsum` [defines an interface for custom path optimizers](https://optimized-einsum.readthedocs.io/en/stable/custom_paths.html),\n", "which other libraries, or any user, can subclass and then supply as the\n", "`optimize` kwarg and will thus be compatible with `quimb`.\n", "The `cotengra` [library](https://github.com/jcmgray/cotengra) offers\n", "'hyper'-optimized contraction paths that are aimed at (and strongly recommended for)\n", "large and complex tensor networks. It provides:\n", "\n", "- The `optimize='hyper'` preset (once `cotengra` is imported)\n", "- The `cotengra.HyperOptimizer` single-shot path optimizer, (like `RandomGreedy` above)\n", "- The `cotengra.ReusableHyperOptimizer`, which can be used for many contractions, and caches the results (optionally to disk)\n", "\n", "The last is probably the most practical so we'll demonstrate it here:" ] }, { "cell_type": "code", "execution_count": 43, "id": "357d665c-7248-4408-b89f-5efecd03e4a9", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "F=4.66 C=5.98 S=10.00 P=11.63: 100%|██████████| 16/16 [00:15<00:00, 1.06it/s]\n" ] }, { "data": { "text/plain": [ "{'flops': 45202, 'write': 14211, 'size': 1024}" ] }, "execution_count": 43, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import cotengra as ctg\n", "\n", "# the kwargs ReusableHyperOptimizer take are the same as HyperOptimizer\n", "opt = ctg.ReusableHyperOptimizer(\n", " max_repeats=16,\n", " reconf_opts={},\n", " parallel=False,\n", " progbar=True,\n", " # directory=True, # if you want a persistent path cache\n", ")\n", "\n", "rehs = circ.local_expectation_rehearse(ZZ, where, optimize=opt)\n", "tn, tree = rehs[\"tn\"], rehs[\"tree\"]\n", "tree.contract_stats()" ] }, { "cell_type": "markdown", "id": "b600436e-fc4a-49ae-a11a-aca616c361af", "metadata": {}, "source": [ "We can see even for this small contraction it has improved on the `RandomGreedy` path cost.\n", "We could use `info.path` here but since we have a `ReusableHyperOptimizer` path\n", "optimizer, this second time its called on the same contraction it will simply get\n", "the path from its cache anway:" ] }, { "cell_type": "code", "execution_count": 44, "id": "f88f43e7-d17e-4784-ae2b-27a179144805", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "70.6 ms ± 4.69 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n" ] } ], "source": [ "%%timeit\n", "circ.local_expectation(ZZ, where, optimize=opt)" ] }, { "cell_type": "markdown", "id": "7ef2ec50-3db8-4178-a7b5-6ec7adf86aa6", "metadata": {}, "source": [ "Again, since the main contraction is very small, we don't see any real improvement." ] }, { "cell_type": "markdown", "id": "58b45f82-5ba8-48b5-b3c9-6dd56a7ae625", "metadata": {}, "source": [ "`cotengra` also has a `ContractionTree` object for manipulating and visualizing\n", "the contraction paths found." ] }, { "cell_type": "code", "execution_count": 45, "id": "6175b264-9abe-4112-bfff-3c4d7c01fd3d", "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "" ], "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" }, { "data": { "text/plain": [ "(
, )" ] }, "execution_count": 45, "metadata": {}, "output_type": "execute_result" } ], "source": [ "tree.plot_tent(order=True)" ] }, { "cell_type": "markdown", "id": "617dffa5-fdd4-412a-aed6-2e55ca0b36ae", "metadata": {}, "source": [ "Here the, grey network at the bottom is the TN to be contracted, and the tree\n", "above it depicts the series of pairwise contractions and their individual cost\n", "needed to find the output answer (the node at the top).\n", "\n", "See https://cotengra.readthedocs.io/en/latest/visualization.html for other methods:" ] }, { "cell_type": "code", "execution_count": 46, "id": "6005efec-747a-4b8f-9075-16014e5f9946", "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "" ], "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" }, { "data": { "text/plain": [ "(
, )" ] }, "execution_count": 46, "metadata": {}, "output_type": "execute_result" } ], "source": [ "tree.plot_rubberband()" ] }, { "cell_type": "markdown", "id": "a6623df9-ad8f-45be-a42a-1a328ce0da9e", "metadata": { "raw_mimetype": "text/restructuredtext" }, "source": [ "## Performing the Contraction (the `backend` kwarg)\n", "\n", "`quimb` and `opt_einsum` both try and be agnostic to the actual arrays they manipulate / contract.\n", "Currently however, the tensor network `Circuit` constructs and simplifies is made up of `numpy.ndarray` backed tensors since they are all generally very small:" ] }, { "cell_type": "code", "execution_count": 47, "id": "37feee8f-f1ce-4823-b7a8-8ddbca0da395", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{2, 4, 8}" ] }, "execution_count": 47, "metadata": {}, "output_type": "execute_result" } ], "source": [ "{t.size for t in tn}" ] }, { "cell_type": "markdown", "id": "7bcca2e3-c63e-49e9-be2f-8ea96f2299c4", "metadata": {}, "source": [ "When it comes to the actual contraction however, where large tensors will appear, it can be advantageous to\n", "use a different library to perform the contractions. If you specify a ``backend`` kwarg, before contraction,\n", "the arrays will converted to the ``backend``, then the contraction performed, and the result converted back\n", "to ``numpy``.\n", "[The list of available backends is here](https://optimized-einsum.readthedocs.io/en/stable/backends.html#special-gpu-backends-for-numpy-arrays), including:\n", "\n", "\n", "* `cupy`\n", "* `jax` (note this actively defaults to single precision)\n", "* `torch`\n", "* `tensorflow`" ] }, { "cell_type": "markdown", "id": "ca55de97-8b0d-4ec3-9c6a-2ebba9979715", "metadata": {}, "source": [ "Sampling is an excellent candidate for GPU acceleration as the same geometry TNs\n", "are contracted over and over again and since sampling is inherently a low\n", "precision task, single precision arrays are a natural fit." ] }, { "cell_type": "code", "execution_count": 48, "id": "7279cd0b-426e-4431-a866-d0ab8ce35fae", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1001010101\n", "0001011000\n", "0110000111\n", "1110011100\n", "1001011110\n", "1010010111\n", "0010101101\n", "1100100011\n", "1101111101\n", "0111100010\n" ] } ], "source": [ "for b in circ.sample(10, backend=\"jax\", dtype=\"complex64\"):\n", " print(b)" ] }, { "cell_type": "markdown", "id": "b18b3052-d369-4df1-af5e-4afc50f8a39c", "metadata": { "raw_mimetype": "text/restructuredtext" }, "source": [ ":::{note}\n", "Both sampling methods,\n", "[`Circuit.sample`](quimb.tensor.circuit.Circuit.sample) and\n", "[`Circuit.sample_chaotic`](quimb.tensor.circuit.Circuit.sample_chaotic),\n", "default to `dtype='complex64'`. The other methods default\n", "to `dtype='complex128'`.\n", ":::\n", "\n", "## Performance Checklist\n", "\n", "Here's a list of things to check if you want to ensure you are getting the\n", "most performance out of your circuit simulation:\n", "\n", "- If you are sampling:\n", " - [ ] Have you tried the *gate-by-gate* method?\n", " - [ ] How are you grouping the qubits? For sampling, there is a sweet spot for `group_size` usually. For chaotic sampling, you might try the *last* \\$M\\$ marginal qubits rather than the *first*, for example.\n", "- [ ] What contraction path optimizer are you using? If performance isn't great, have you tried [`cotengra`](https://cotengra.readthedocs.io)?\n", " - [ ] if you are using `cotengra` and subtree-reconfiguration / dynamic slicing, have you installed [`cotengrust`](https://github.com/jcmgray/cotengrust)?\n", "- [ ] How are you applying the gates? For example, `gate_opts={'contract': False}` (no decomposition) can be better for diagonal 2-qubit gates.\n", "- [ ] What local simplifications are you using, and in what order? `simplify_sequence='SADCR'` is also good sometimes.\n", "- [ ] If the computation is contraction dominated, can you run it on a GPU?" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3" } }, "nbformat": 4, "nbformat_minor": 4 }