microsoft/qdk
Publicmirrored fromhttps://github.com/microsoft/qdkAvailable
samples/estimation/Dynamics.qs
172lines · modecode
| 1 | /// # Sample |
| 2 | /// Quantum Dynamics |
| 3 | /// |
| 4 | /// # Description |
| 5 | /// This example demonstrates quantum dynamics in a style tailored for |
| 6 | /// resource estimation. The sample is specifically the simulation |
| 7 | /// of an Ising model Hamiltonian on an N1xN2 2D lattice using a |
| 8 | /// fourth-order Trotter Suzuki product formula, assuming |
| 9 | /// a 2D qubit architecture with nearest-neighbor connectivity. |
| 10 | /// The is an example of a program that is not amenable to simulating |
| 11 | /// classically, but can be run through resource estimation to determine |
| 12 | /// what size of quantum system would be needed to solve the problem. |
| 13 | |
| 14 | import Std.Math.*; |
| 15 | import Std.Arrays.*; |
| 16 | |
| 17 | operation Main() : Unit { |
| 18 | // n : Int, m : Int, t: Double, u : Double, tstep : Double |
| 19 | |
| 20 | let n = 10; |
| 21 | let m = 10; |
| 22 | |
| 23 | let J = 1.0; |
| 24 | let g = 1.0; |
| 25 | |
| 26 | let totTime = 30.0; |
| 27 | let dt = 0.9; |
| 28 | |
| 29 | IsingModel2DSim(n, m, J, g, totTime, dt); |
| 30 | } |
| 31 | |
| 32 | /// # Summary |
| 33 | /// The function below creates a sequence containing the rotation angles that will be applied with the two operators used in the expansion of the Trotter-Suzuki formula. |
| 34 | /// # Input |
| 35 | /// ## p (Double) : Constant used for fourth-order formulas |
| 36 | /// |
| 37 | /// ## dt (Double) : Time-step used to compute rotation angles |
| 38 | /// |
| 39 | /// ## J (Double) : coefficient for 2-qubit interactions |
| 40 | /// |
| 41 | /// ## g (Double) : coefficient for transverse field |
| 42 | /// |
| 43 | /// # Output |
| 44 | /// ## values (Double[]) : The list of rotation angles to be applies in sequence with the corresponding operators |
| 45 | /// |
| 46 | function SetAngleSequence(p : Double, dt : Double, J : Double, g : Double) : Double[] { |
| 47 | |
| 48 | let len1 = 3; |
| 49 | let len2 = 3; |
| 50 | let valLength = 2 * len1 + len2 + 1; |
| 51 | mutable values = [0.0, size = valLength]; |
| 52 | |
| 53 | let val1 = J * p * dt; |
| 54 | let val2 = -g * p * dt; |
| 55 | let val3 = J * (1.0 - 3.0 * p) * dt / 2.0; |
| 56 | let val4 = g * (1.0 - 4.0 * p) * dt / 2.0; |
| 57 | |
| 58 | for i in 0..len1 { |
| 59 | |
| 60 | if (i % 2 == 0) { |
| 61 | set values w/= i <- val1; |
| 62 | } else { |
| 63 | set values w/= i <- val2; |
| 64 | } |
| 65 | |
| 66 | } |
| 67 | |
| 68 | for i in len1 + 1..len1 + len2 { |
| 69 | if (i % 2 == 0) { |
| 70 | set values w/= i <- val3; |
| 71 | } else { |
| 72 | set values w/= i <- val4; |
| 73 | } |
| 74 | } |
| 75 | |
| 76 | for i in len1 + len2 + 1..valLength - 1 { |
| 77 | if (i % 2 == 0) { |
| 78 | set values w/= i <- val1; |
| 79 | } else { |
| 80 | set values w/= i <- val2; |
| 81 | } |
| 82 | } |
| 83 | return values; |
| 84 | } |
| 85 | |
| 86 | /// # Summary |
| 87 | /// Applies e^-iX(theta) on all qubits in the 2D lattice as part of simulating the transverse field in the Ising model |
| 88 | /// # Input |
| 89 | /// ## n (Int) : Lattice size for an n x n lattice |
| 90 | /// |
| 91 | /// ## qArr (Qubit[][]) : Array of qubits representing the lattice |
| 92 | /// |
| 93 | /// ## theta (Double) : The angle/time-step for which the unitary simulation is done. |
| 94 | /// |
| 95 | operation ApplyAllX(n : Int, qArr : Qubit[][], theta : Double) : Unit { |
| 96 | // This applies `Rx` with an angle of `2.0 * theta` to all qubits in `qs` |
| 97 | // using partial application |
| 98 | for row in 0..n - 1 { |
| 99 | ApplyToEach(Rx(2.0 * theta, _), qArr[row]); |
| 100 | } |
| 101 | } |
| 102 | |
| 103 | /// # Summary |
| 104 | /// Applies e^-iP(theta) where P = Z o Z as part of the repulsion terms. |
| 105 | /// # Input |
| 106 | /// ## n, m (Int, Int) : Lattice sizes for an n x m lattice |
| 107 | /// |
| 108 | /// ## qArr (Qubit[]) : Array of qubits representing the lattice |
| 109 | /// |
| 110 | /// ## theta (Double) : The angle/time-step for which unitary simulation is done. |
| 111 | /// |
| 112 | /// ## dir (Bool) : Direction is true for vertical direction. |
| 113 | /// |
| 114 | /// ## grp (Bool) : Group is true for odd starting indices |
| 115 | /// |
| 116 | operation ApplyDoubleZ(n : Int, m : Int, qArr : Qubit[][], theta : Double, dir : Bool, grp : Bool) : Unit { |
| 117 | let start = grp ? 1 | 0; // Choose either odd or even indices based on group number |
| 118 | let P_op = [PauliZ, PauliZ]; |
| 119 | let c_end = dir ? m - 1 | m - 2; |
| 120 | let r_end = dir ? m - 2 | m - 1; |
| 121 | |
| 122 | for row in 0..r_end { |
| 123 | for col in start..2..c_end { |
| 124 | // Iterate through even or odd columns based on `grp` |
| 125 | |
| 126 | let row2 = dir ? row + 1 | row; |
| 127 | let col2 = dir ? col | col + 1; |
| 128 | |
| 129 | Exp(P_op, theta, [qArr[row][col], qArr[row2][col2]]); |
| 130 | } |
| 131 | } |
| 132 | } |
| 133 | |
| 134 | /// # Summary |
| 135 | /// The main function that takes in various parameters and calls the operations needed to simulate fourth order Trotterizatiuon of the Ising Hamiltonian for a given time-step |
| 136 | /// # Input |
| 137 | /// ## N1, N2 (Int, Int) : Lattice sizes for an N1 x N2 lattice |
| 138 | /// |
| 139 | /// ## J (Double) : coefficient for 2-qubit interactions |
| 140 | /// |
| 141 | /// ## g (Double) : coefficient for transverse field |
| 142 | /// |
| 143 | /// ## totTime (Double) : The total time-step for which unitary simulation is done. |
| 144 | /// |
| 145 | /// ## dt (Double) : The time the simulation is done for each timestep |
| 146 | /// |
| 147 | operation IsingModel2DSim(N1 : Int, N2 : Int, J : Double, g : Double, totTime : Double, dt : Double) : Unit { |
| 148 | |
| 149 | use qs = Qubit[N1 * N2]; |
| 150 | let qubitArray = Chunks(N2, qs); // qubits are re-arranged to be in an N1 x N2 array |
| 151 | |
| 152 | let p = 1.0 / (4.0 - 4.0^(1.0 / 3.0)); |
| 153 | let t = Ceiling(totTime / dt); |
| 154 | |
| 155 | let seqLen = 10 * t + 1; |
| 156 | |
| 157 | let angSeq = SetAngleSequence(p, dt, J, g); |
| 158 | |
| 159 | for i in 0..seqLen - 1 { |
| 160 | let theta = (i == 0 or i == seqLen - 1) ? J * p * dt / 2.0 | angSeq[i % 10]; |
| 161 | |
| 162 | // for even indexes |
| 163 | if i % 2 == 0 { |
| 164 | ApplyAllX(N1, qubitArray, theta); |
| 165 | } else { |
| 166 | // iterate through all possible combinations for `dir` and `grp`. |
| 167 | for (dir, grp) in [(true, true), (true, false), (false, true), (false, false)] { |
| 168 | ApplyDoubleZ(N1, N2, qubitArray, theta, dir, grp); |
| 169 | } |
| 170 | } |
| 171 | } |
| 172 | } |
| 173 | |