diff --git a/tensor_networks_solutions.ipynb b/tensor_networks_solutions.ipynb new file mode 100644 index 0000000..52a2b45 --- /dev/null +++ b/tensor_networks_solutions.ipynb @@ -0,0 +1,701 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "63ef3c93", + "metadata": {}, + "source": [ + "# Efficient quantum simulation using Tensor Network states" + ] + }, + { + "cell_type": "markdown", + "id": "1b737328", + "metadata": {}, + "source": [ + "The aim of this notebook is to demonstrate the impact of the Tensor Network state representation\n", + "on computational complexity in quantum computing simulations. During the morning lecture, the asymptotic complexity of\n", + "TN methods was discussed in an abstract manner, and now we will see, through some concrete examples via the ITensors package\n", + "precisely what the practical difference in runtimes is." + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "id": "32dd2d09", + "metadata": {}, + "outputs": [], + "source": [ + "using LinearAlgebra\n", + "using ITensors" + ] + }, + { + "cell_type": "markdown", + "id": "b06c335f", + "metadata": {}, + "source": [ + "# Exercise 1: 5 minutes " + ] + }, + { + "cell_type": "markdown", + "id": "4ddef59e", + "metadata": {}, + "source": [ + "Complete the code in the following function to compute the Kronecker product ⊗ of vectors/matrices a,b.\n", + "Note that the Kronecker product gives the abstract tensor product expressed in a particular basis.\n", + "https://en.wikipedia.org/wiki/Kronecker_product" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "id": "0bf4f90a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "tensor (generic function with 1 method)" + ] + }, + "execution_count": 38, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "function tensor(a,b)\n", + " return kron(a,b)\n", + "end" + ] + }, + { + "cell_type": "markdown", + "id": "16ee9e64", + "metadata": {}, + "source": [ + "run the below cell to test your implementation of tensor" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "id": "5679cee3", + "metadata": {}, + "outputs": [], + "source": [ + "function test_tensor()\n", + " @assert tensor([1 2; 3 4], [5 6; 7 8]) == [5 6 10 12; 7 8 14 16; 15 18 20 24; 21 24 28 32]\n", + " @assert tensor([1, 2], [3, 4]) == [3, 4, 6, 8]\n", + " return nothing\n", + "end\n", + "\n", + "test_tensor()" + ] + }, + { + "cell_type": "markdown", + "id": "ebd1c307", + "metadata": {}, + "source": [ + "hint: there is an inbuilt kron function in the language" + ] + }, + { + "cell_type": "markdown", + "id": "ed16c698", + "metadata": {}, + "source": [ + "## Key takeaway: taking the tensor product multiplies all the dimensions of the inputs" + ] + }, + { + "cell_type": "markdown", + "id": "d4e4fffb", + "metadata": {}, + "source": [ + "# Exercise 2: 5 minutes" + ] + }, + { + "cell_type": "markdown", + "id": "a1ba7336", + "metadata": {}, + "source": [ + "Using the represenation |0> = [1, 0] and |1> = [0, 1], compute for n qubits,\n", + "GHZ state: (|0> ⊗ ... ⊗ |0>) + (|1> ⊗ ... |1>)\n", + "Hadamard state: (|0> + |1>) ⊗ ... ⊗ (|0> + |1>)>\n", + "Note that both these states are very relevant in quantum computing:\n", + "The GHZ state is the unmagnetized ground state of the Ising model,\n", + "The Hadamard state is the input in many early quantum algorithms." + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "id": "b25b362a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "hadamard_state (generic function with 1 method)" + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "function ghz_state(n)\n", + " return foldl(tensor, [[1,0] for _ in 1:n]) + foldl(tensor, [[0,1] for _ in 1:n])\n", + "end\n", + "\n", + "function hadamard_state(n)\n", + " return foldl(tensor, [[1,1] for _ in 1:n])\n", + "end" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "id": "de6be729", + "metadata": {}, + "outputs": [], + "source": [ + "function test_states()\n", + " @assert ghz_state(3) == [1, 0, 0, 0, 0, 0, 0, 1]\n", + " @assert hadamard_state(3) == [1, 1, 1, 1, 1, 1, 1, 1]\n", + " return nothing\n", + "end\n", + "\n", + "test_states()" + ] + }, + { + "cell_type": "markdown", + "id": "c307db04", + "metadata": {}, + "source": [ + "hint: recursively call tensor, for example by using foldl(tensor, .)" + ] + }, + { + "cell_type": "markdown", + "id": "6d698325", + "metadata": {}, + "source": [ + "## Key takeaway: The GHZ state is very sparse in this representation, the Hadamard state is not" + ] + }, + { + "cell_type": "markdown", + "id": "0466a7c5", + "metadata": {}, + "source": [ + "# Exercise 3: 5 minutes" + ] + }, + { + "cell_type": "markdown", + "id": "35833e67", + "metadata": {}, + "source": [ + "Print the GHZ and Hadamard states for 3 qubits.\n", + "Compute the time it takes to do the inner product \n", + "for n ranging up to 25-30 (depending on the speed of your machine)" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "id": "4cb35918", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[1, 0, 0, 0, 0, 0, 0, 1][1, 1, 1, 1, 1, 1, 1, 1][3.81e-7, 5.0e-8, 2.0e-8, 4.0e-8, 5.0e-8, 4.0e-8, 5.0e-8, 6.0e-8, 9.1e-8, 1.6e-7, 2.91e-7, 5.71e-7, 1.143e-6, 2.154e-6, 4.348e-6, 1.3806e-5, 3.3273e-5, 0.000102013, 0.000218073, 0.000268298, 0.001043047, 0.002357017, 0.005072822, 0.007236002, 0.014415698, 0.030268007, 0.058763015, 0.11602294, 0.218377929, 0.447584456]" + ] + } + ], + "source": [ + "print(ghz_state(3))\n", + "print(hadamard_state(3))\n", + "\n", + "function time_inner(n)\n", + " state = hadamard_state(n)\n", + " t = @timed dot(state, state) #replace this with a computation of inner product\n", + " return t[:time]\n", + "end\n", + "\n", + "print([time_inner(n) for n in 1:30])" + ] + }, + { + "cell_type": "markdown", + "id": "3a3737c1", + "metadata": {}, + "source": [ + "hint: use LinearAlgebra.dot" + ] + }, + { + "cell_type": "markdown", + "id": "bf1fe27f", + "metadata": {}, + "source": [ + "## Key takeaway: \n", + "## The length of both the GHZ and Hadamard states increases as 2^n\n", + "## The time to compute the inner product increases exponentially in n.\n", + "## Even on very big computers, going above 40-50 qubits is impossible." + ] + }, + { + "cell_type": "markdown", + "id": "65362811", + "metadata": {}, + "source": [ + "To be able to simulate large numbers of qubits, a better representation of the relevant states in the Hilbert space is needed.\n", + "For a state like the GHZ state, which is sparse in the basis above, it should be obvious that such a represenation exists.\n", + "That there is a good representation that encapsulates all interesting states (such as the Hadamard state) is not obvious.\n", + "\n", + "During the morning lecture, it was explained that the Tensor Network representation seems to fit this bill, and why.\n", + "We will now redo the above exercises in the Tensor Network representation, and see the benefits it provides." + ] + }, + { + "cell_type": "markdown", + "id": "48fb24e7", + "metadata": {}, + "source": [ + "# Exercise 4: 15 minutes" + ] + }, + { + "cell_type": "markdown", + "id": "3c2c5377", + "metadata": {}, + "source": [ + "Construct tensor network representations of the\n", + "GHZ state: (|0> ⊗ ... ⊗ |0>) + (|1> ⊗ ... |1>)\n", + "Hadamard state: (|0> + |1>) ⊗ ... ⊗ (|0> + |1>)>\n", + "In ITensors such a representation is called an MPS (matrix product state)" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "id": "d66cf468", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "tn_hadamard_state (generic function with 1 method)" + ] + }, + "execution_count": 43, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "function tn_ghz_state(n)\n", + " sites = siteinds(\"S=1/2\", n)\n", + " states_up = [\"Up\" for _ in 1:n]\n", + " states_dn = [\"Dn\" for _ in 1:n]\n", + " return MPS(sites, states_dn) + MPS(sites, states_up)\n", + "end\n", + "\n", + "function tn_hadamard_state(n)\n", + " sites = siteinds(\"S=1/2\", n)\n", + " state = randomMPS(sites) #this constructs a randomly initialized product wave-function in TN form\n", + " for s in 1:n #manually set each qubit to the [1, 1] state\n", + " state[s][1] = 1\n", + " state[s][2] = 1\n", + " end\n", + " return state\n", + "end" + ] + }, + { + "cell_type": "code", + "execution_count": 76, + "id": "c890fa37", + "metadata": {}, + "outputs": [], + "source": [ + "function test_tn_states()\n", + " N = 5 \n", + "\n", + " function elt(ϕ, inds)\n", + " s = siteinds(ϕ)\n", + " V = ITensor(1.)\n", + " for j=1:N\n", + " V *= (ϕ[j]*state(s[j],inds[j]))\n", + " end\n", + " return scalar(V)\n", + " end\n", + "\n", + " ψ = tn_ghz_state(N)\n", + " @assert elt(ψ, [1,1,1,1,1]) ≈ 1 \n", + " @assert elt(ψ, [2,2,2,2,2]) ≈ 1 \n", + " @assert elt(ψ, [1,1,1,2,1]) ≈ 0\n", + " \n", + "\n", + " ψ = tn_hadamard_state(N)\n", + " @assert elt(ψ, [1,1,1,2,1]) ≈ 1 \n", + " @assert elt(ψ, [1,2,1,2,2]) ≈ 1\n", + "\n", + " return nothing\n", + "end\n", + "test_tn_states()" + ] + }, + { + "cell_type": "markdown", + "id": "d2f2b481", + "metadata": {}, + "source": [ + "hint1: You can create an MPS using the states constructed in Exercise 2, and an array of siteinds\n", + " https://itensor.github.io/ITensors.jl/stable/examples/MPSandMPO.html" + ] + }, + { + "cell_type": "markdown", + "id": "d9665529", + "metadata": {}, + "source": [ + "## Key takeaway: The GHZ and Hadamard state have representations with low bond dimensions. This is what makes computations with them very efficient" + ] + }, + { + "cell_type": "markdown", + "id": "054fba57", + "metadata": {}, + "source": [ + "# Exercise 5: 15 minutes" + ] + }, + { + "cell_type": "markdown", + "id": "110ae1eb", + "metadata": {}, + "source": [ + "Print the GHZ and Hadamard states for 3 qubits\n", + "Compute the time it takes to do the inner product \n", + "for n ranging up to 100\n", + "Do this using the TN representation of the states" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "id": "657598b0", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MPS\n", + "[1] ((dim=2|id=53|\"S=1/2,Site,n=1\"), (dim=2|id=6|\"Link,l=1\"))\n", + "[2] ((dim=2|id=709|\"S=1/2,Site,n=2\"), (dim=2|id=413|\"Link,l=2\"), (dim=2|id=6|\"Link,l=1\"))\n", + "[3] ((dim=2|id=810|\"S=1/2,Site,n=3\"), (dim=2|id=413|\"Link,l=2\"))\n", + "MPS\n", + "[1] ((dim=2|id=324|\"S=1/2,Site,n=1\"), (dim=1|id=140|\"Link,l=1\"))\n", + "[2] ((dim=1|id=140|\"Link,l=1\"), (dim=2|id=758|\"S=1/2,Site,n=2\"), (dim=1|id=580|\"Link,l=2\"))\n", + "[3] ((dim=1|id=580|\"Link,l=2\"), (dim=2|id=780|\"S=1/2,Site,n=3\"))\n", + "[6.861e-5, 6.2609e-5, 7.5743e-5, 7.8349e-5, 8.5351e-5, 0.000103546, 0.000131349, 0.000156616, 0.00017379, 0.000183818, 0.000199489, 0.000224856, 0.000243361, 0.000259582, 0.000286734, 0.000307352, 0.000328823, 0.000350644, 0.000400329, 0.000416589, 0.000436698, 0.000452738, 0.000471284, 0.000482514, 0.000509936, 0.000536506, 0.000560452, 0.000577805, 0.000598394, 0.000621248, 0.00072271, 0.000664429, 0.000682884, 0.000713311, 0.00073359, 0.00074972, 0.000775029, 0.000793554, 0.000816908, 0.000854339, 0.000893243, 0.000926516, 0.000917999, 0.000938649, 0.000962103, 0.000981039, 0.001004133, 0.001026735, 0.001031615, 0.001062974, 0.001074486, 0.00108687, 0.001122237, 0.001124811, 0.001183153, 0.001227536, 0.001194904, 0.001320352, 0.001254608, 0.001251511, 0.001278293, 0.001316104, 0.001327676, 0.001351331, 0.001360097, 0.001460207, 0.001462632, 0.001452903, 0.001481237, 0.001522404, 0.001500944, 0.001492549, 0.001515482, 0.001541551, 0.001559816, 0.001668581, 0.001541441, 0.001596065, 0.001597287, 0.001679643, 0.001622184, 0.001591536, 0.001605672, 0.001772518, 0.001638214, 0.001677519, 0.001677479, 0.001703569, 0.001719869, 0.001762108, 0.001837001, 0.001832823, 0.001798418, 0.001825189, 0.00189472, 0.00184737, 0.001870564, 0.001942972, 0.001986465, 0.001958131]" + ] + } + ], + "source": [ + "print(tn_ghz_state(3))\n", + "print(tn_hadamard_state(3))\n", + "\n", + "function tn_time_inner(n)\n", + " state = tn_hadamard_state(n)\n", + " t = @timed inner(state, state) #replace this with a computation of inner product\n", + " return t[:time]\n", + "end\n", + "\n", + "print([tn_time_inner(n) for n in 1:100])" + ] + }, + { + "cell_type": "markdown", + "id": "c4decb51", + "metadata": {}, + "source": [ + "hint:\n", + "Use ITensors.inner.\n", + "If your code takes too long, construct the GHZ state using the solution code of Exercise 4." + ] + }, + { + "cell_type": "markdown", + "id": "de28eec8", + "metadata": {}, + "source": [ + "# Exercise 6: 15 minutes" + ] + }, + { + "cell_type": "markdown", + "id": "2538e35f", + "metadata": {}, + "source": [ + "Construct a tensor network representation of the HWA quantum circuit from the lecture for 3 qubits.\n", + "That is, the following sequence of gates (Ry, Rx, Ry, CNOTeven, CNOTodd)\n", + "where, in the 3 qubit case, CNOTeven is a CNOT gate between qubits 1 and 2, and CNOTodd is a CNOT gate between qubits 2 and 3." + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "id": "a0dadd4d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "hwa_layer (generic function with 1 method)" + ] + }, + "execution_count": 46, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Ry(theta) = cos(theta/2)*[1 0; 0 1] + im*sin(theta/2)*[0 -im; im 0]\n", + "Rx(theta) = cos(theta/2)*[1 0; 0 1] + im*sin(theta/2)*[0 1; 1 0]\n", + "ITensors.op(::OpName\"rotations\", ::SiteType\"Qubit\"; t1::Number, t2::Number, t3::Number) = Ry(t3)*Rx(t2)*Ry(t1)\n", + "\n", + "function hwa_layer(sites, angles::Vector{<:Number})\n", + " layer = Prod{Op}()\n", + " for i in 1:length(sites)\n", + " layer = Op(\"rotations\", i; t1=angles[1+3*(i-1)], t2=angles[2+3*(i-1)], t3=angles[3+3*(i-1)])*layer\n", + " end\n", + " layer = Op(\"CNOT\", 1, 2)*layer\n", + " layer = Op(\"CNOT\", 2, 3)*layer\n", + " return Prod{ITensor}(layer, sites)\n", + "end" + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "id": "700ab565", + "metadata": {}, + "outputs": [], + "source": [ + "function test_hwa_layer()\n", + " N = 3\n", + " function elt(ϕ, inds)\n", + " s = siteinds(ϕ)\n", + " V = ITensor(1.)\n", + " for j=1:N\n", + " V *= (ϕ[j]*state(s[j],inds[j]))\n", + " end\n", + " return scalar(V)\n", + " end\n", + " sites = siteinds(\"S=1/2\", N)\n", + " in = MPS(sites, [\"Dn\", \"Up\", \"Dn\"])\n", + " hwa = hwa_layer(sites, [pi/2, pi/2, -pi/2, pi/2, pi/2, -pi/2, pi/2, pi/2, -pi/2])\n", + " out = apply(hwa, in)\n", + " @assert elt(out,[2,2,1]) ≈ (sqrt(2)+im*sqrt(2))/2\n", + " \n", + " return nothing\n", + "end\n", + "\n", + "test_hwa_layer()" + ] + }, + { + "cell_type": "markdown", + "id": "188d759d", + "metadata": {}, + "source": [ + "hint1: You can see how to chain elementary gates into a circuit here: https://github.com/ITensor/ITensors.jl/blob/main/examples/autodiff/ops/vqe.jl\n", + "hint2: In the above file look at layer, variational_circuit, and lines 77-79" + ] + }, + { + "cell_type": "markdown", + "id": "003a4139", + "metadata": {}, + "source": [ + "## Key takeaway: Every quantum circuit has a natural Tensor Network representation" + ] + }, + { + "cell_type": "markdown", + "id": "6a81455e", + "metadata": {}, + "source": [ + "# Exercise 7: optional" + ] + }, + { + "cell_type": "markdown", + "id": "f9acd467", + "metadata": {}, + "source": [ + "Create a randomly initialized MPS, and orthogonalize it (put it in canonical form).\n", + "Experiment with the effect on the bond dimension of the state." + ] + }, + { + "cell_type": "code", + "execution_count": 74, + "id": "e7b7bae7", + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MPS\n", + "[1] ((dim=2|id=822|\"Site,n=1\"), (dim=5|id=112|\"Link,l=1\"))\n", + "[2] ((dim=5|id=112|\"Link,l=1\"), (dim=2|id=839|\"Site,n=2\"), (dim=5|id=138|\"Link,l=2\"))\n", + "[3] ((dim=5|id=138|\"Link,l=2\"), (dim=2|id=966|\"Site,n=3\"), (dim=4|id=99|\"Link,l=3\"))\n", + "[4] ((dim=4|id=99|\"Link,l=3\"), (dim=2|id=82|\"Site,n=4\"), (dim=2|id=38|\"Link,l=4\"))\n", + "[5] ((dim=2|id=38|\"Link,l=4\"), (dim=2|id=257|\"Site,n=5\"))\n", + "MPS\n", + "[1] ((dim=2|id=822|\"Site,n=1\"), (dim=2|id=665|\"Link,l=1\"))\n", + "[2] ((dim=2|id=839|\"Site,n=2\"), (dim=2|id=665|\"Link,l=1\"), (dim=4|id=289|\"Link,l=2\"))\n", + "[3] ((dim=2|id=966|\"Site,n=3\"), (dim=4|id=99|\"Link,l=3\"), (dim=4|id=289|\"Link,l=2\"))\n", + "[4] ((dim=4|id=99|\"Link,l=3\"), (dim=2|id=82|\"Site,n=4\"), (dim=2|id=38|\"Link,l=4\"))\n", + "[5] ((dim=2|id=38|\"Link,l=4\"), (dim=2|id=257|\"Site,n=5\"))\n", + "[-0.2238266996370215 -0.9746289594146064; -0.9746289594146064 0.22382669963702162]\n", + "[-0.3155864641498065 -0.6905506524335345; -0.37302565158085976 0.5332887054229801;;; 0.5089309391412836 0.1128986991206278; -0.8415900355203036 -0.14131310991756857;;; -0.43470091535148925 -0.3393057823637782; -0.17130351348912531 -0.8164323649350038;;; 0.6726288701972074 -0.6287012535497241; 0.35104583980142806 -0.17050500027959403]\n" + ] + } + ], + "source": [ + "sites = siteinds(2, 5)\n", + "ψ = randomMPS(sites; linkdims = 5)\n", + "print(ψ)\n", + "orthogonalize!(ψ, 3)\n", + "print(ψ)\n", + "a = matrix(ψ[1], inds(ψ[1]))\n", + "println(a)\n", + "b = array(ψ[2], inds(ψ[2]))\n", + "println(b)" + ] + }, + { + "cell_type": "markdown", + "id": "e056788e", + "metadata": {}, + "source": [ + "hint:\n", + "call orthogonalize or orthogonalize!" + ] + }, + { + "cell_type": "markdown", + "id": "e4dd25b9", + "metadata": {}, + "source": [ + "## key takeaway: The bond dimension of a random state can already be quite well reduced by using the canonical form, demonstrating how efficient the Tensor Network representation is." + ] + }, + { + "cell_type": "markdown", + "id": "5d8ced3d", + "metadata": {}, + "source": [ + "# Exercise 8: optional" + ] + }, + { + "cell_type": "markdown", + "id": "75188bf9", + "metadata": {}, + "source": [ + "Verify that all the tensors to the left of the orthogonality center are left-orthogonal (contracting the tensor with itself along the site index, and the bond index pointing away from the center yields the identity matrix), and the tensors to the right of the orthogonality center are right-orthogonal." + ] + }, + { + "cell_type": "code", + "execution_count": 75, + "id": "35d881b9", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MPS\n", + "[1] ((dim=2|id=386|\"Site,n=1\"), (dim=2|id=257|\"Link,l=1\"))\n", + "[2] ((dim=2|id=769|\"Site,n=2\"), (dim=2|id=257|\"Link,l=1\"), (dim=4|id=383|\"Link,l=2\"))\n", + "[3] ((dim=2|id=732|\"Site,n=3\"), (dim=4|id=353|\"Link,l=3\"), (dim=4|id=383|\"Link,l=2\"))\n", + "[4] ((dim=4|id=353|\"Link,l=3\"), (dim=2|id=894|\"Site,n=4\"), (dim=2|id=320|\"Link,l=4\"))\n", + "[5] ((dim=2|id=320|\"Link,l=4\"), (dim=2|id=667|\"Site,n=5\"))\n", + "\n", + "[1.0 -1.816482856755442e-17; -1.816482856755442e-17 1.0]\n", + "[0.9999999999999999 -2.7386369221720604e-17 -4.6117694078128315e-17 -4.143151684907072e-17; -2.7386369221720604e-17 0.9999999999999998 1.332795245353943e-16 7.434189976907319e-18; -4.6117694078128315e-17 1.332795245353943e-16 0.9999999999999999 -2.7670529067836217e-17; -4.143151684907072e-17 7.434189976907319e-18 -2.7670529067836217e-17 1.0]\n" + ] + } + ], + "source": [ + "sites = siteinds(2, 5)\n", + "ψ = randomMPS(sites; linkdims = 5)\n", + "orthogonalize!(ψ, 3)\n", + "println(ψ)\n", + "a = ψ[1]\n", + "b = deepcopy(a)\n", + "prime!(b; tags=\"l=1\")\n", + "c = matrix(a*b)\n", + "println(c)\n", + "d = ψ[2]\n", + "e = deepcopy(d)\n", + "prime!(e; tags=\"l=2\")\n", + "f = matrix(d*e)\n", + "println(f)" + ] + }, + { + "cell_type": "markdown", + "id": "f481ffa2", + "metadata": {}, + "source": [ + "hint:\n", + "you can copy any tensor, and then prime some of its indexes. Contracting the tensor with its copy will contract along the unprimed indices. https://itensor.github.io/ITensors.jl/stable/ITensorType.html#Priming_and_tagging_ITensor" + ] + }, + { + "cell_type": "markdown", + "id": "d4edeb9e", + "metadata": {}, + "source": [ + "## Key takeaway: the orthogonality properties allow one to save computational resources, since many of the contractions occuring on measurement of local operators are guaranteed to be trivial" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Julia 1.8.1", + "language": "julia", + "name": "julia-1.8" + }, + "language_info": { + "file_extension": ".jl", + "mimetype": "application/julia", + "name": "julia", + "version": "1.8.1" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}