microsoft/qdk
Publicmirrored fromhttps://github.com/microsoft/qdkAvailable
samples/algorithms/BernsteinVazirani.qs
167lines Β· modecode
| 1 | /// # Sample |
| 2 | /// Bernstein-Vazirani Algorithm |
| 3 | /// |
| 4 | /// # Description |
| 5 | /// The Bernstein-Vazirani algorithm determines the value of a bit string |
| 6 | /// encoded in a function. |
| 7 | /// |
| 8 | /// This Q# program implements the Bernstein-Vazirani algorithm. |
| 9 | import Std.Arrays.*; |
| 10 | import Std.Convert.*; |
| 11 | import Std.Diagnostics.*; |
| 12 | import Std.Math.*; |
| 13 | import Std.Measurement.*; |
| 14 | |
| 15 | operation Main() : Int[] { |
| 16 | // Consider a function π(π₯β) on bitstrings π₯β = (π₯β, β¦, π₯βββ) of the form |
| 17 | // π(π₯β) β Ξ£α΅’ π₯α΅’ πα΅’ |
| 18 | // where πβ = (πβ, β¦, πβββ) is an unknown bitstring that determines the |
| 19 | // parity of π. |
| 20 | |
| 21 | // The BernsteinβVazirani algorithm allows determining π given a |
| 22 | // quantum operation that implements |
| 23 | // |π₯βͺ|π¦βͺ β¦ |π₯βͺ|π¦ β π(π₯)βͺ. |
| 24 | |
| 25 | // The entry point function of this program, `Main`, shows how to use |
| 26 | // the `BernsteinVazirani` operation to determine the value of various |
| 27 | // integers whose bits describe π. |
| 28 | let nQubits = 10; |
| 29 | |
| 30 | // Use the BernsteinβVazirani algorithm to determine the bit strings |
| 31 | // that various integers represent. |
| 32 | let integers = [127, 238, 512]; |
| 33 | mutable decodedIntegers = []; |
| 34 | for integer in integers { |
| 35 | // Create an operation that encodes a bit string represented by an |
| 36 | // integer as a parity operation. |
| 37 | let parityOperation = EncodeIntegerAsParityOperation(integer); |
| 38 | |
| 39 | // Use the parity operation as input to the Bernstein-Vazirani |
| 40 | // algorithm to determine the bit string. |
| 41 | let decodedBitString = BernsteinVazirani(parityOperation, nQubits); |
| 42 | let decodedInteger = ResultArrayAsInt(decodedBitString); |
| 43 | decodedIntegers += [decodedInteger]; |
| 44 | } |
| 45 | |
| 46 | return decodedIntegers; |
| 47 | } |
| 48 | |
| 49 | /// # Summary |
| 50 | /// Validates the implementation of the Bernstein-Vazirani algorithm by |
| 51 | /// comparing the decoded integers to the expected values. |
| 52 | /// This also demonstrates how to use the `@Test` attribute to define a test operation. |
| 53 | @Test() |
| 54 | operation ValidateBernsteinVazirani() : Unit { |
| 55 | // To see how tests verify the behavior, try changing either the algorithm or the expected values |
| 56 | // in the assertion below and observe the test failure. |
| 57 | let integers = [127, 238, 512]; |
| 58 | let results = Main(); |
| 59 | Fact(results == integers, $"Decoded integers {results} do not match expected values {integers}"); |
| 60 | } |
| 61 | |
| 62 | /// # Summary |
| 63 | /// This operation implements the Bernstein-Vazirani quantum algorithm. |
| 64 | /// This algorithm computes for a given Boolean function that is promised to |
| 65 | /// be a parity π(π₯β, β¦, π₯βββ) = Ξ£α΅’ πα΅’ π₯α΅’ a result in the form of a bit |
| 66 | /// vector (πβ, β¦, πβββ) corresponding to the parity function. |
| 67 | /// Note that it is promised that the function is actually a parity |
| 68 | /// function. |
| 69 | /// |
| 70 | /// # Input |
| 71 | /// ## Uf |
| 72 | /// A quantum operation that implements |π₯βͺ|π¦βͺ β¦ |π₯βͺ|π¦ β π(π₯)βͺ, |
| 73 | /// where π is a Boolean function that implements a parity Ξ£α΅’ πα΅’ π₯α΅’. |
| 74 | /// ## n |
| 75 | /// The number of bits in the input register |π₯βͺ. |
| 76 | /// |
| 77 | /// # Output |
| 78 | /// An array of type `Result[]` that contains the parity πβ = (πβ, β¦, πβββ). |
| 79 | /// |
| 80 | /// # See Also |
| 81 | /// - For details see Section 1.4.3 of Nielsen & Chuang. |
| 82 | /// |
| 83 | /// # References |
| 84 | /// - [ *Ethan Bernstein and Umesh Vazirani*, |
| 85 | /// SIAM J. Comput., 26(5), 1411β1473, 1997 ] |
| 86 | /// (https://doi.org/10.1137/S0097539796300921) |
| 87 | operation BernsteinVazirani(Uf : ((Qubit[], Qubit) => Unit), n : Int) : Result[] { |
| 88 | // We allocate n + 1 clean qubits. Note that the function Uf is defined |
| 89 | // on inputs of the form (x, y), where x has n bits and y has 1 bit. |
| 90 | use queryRegister = Qubit[n]; |
| 91 | use target = Qubit(); |
| 92 | |
| 93 | // The last qubit needs to be flipped so that the function will actually |
| 94 | // be computed into the phase when Uf is applied. |
| 95 | X(target); |
| 96 | |
| 97 | within { |
| 98 | // Now, a Hadamard transform is applied to each of the qubits. As |
| 99 | // the last step before the measurement, a Hadamard transform is |
| 100 | // applied to all qubits except last one. We could apply the |
| 101 | // transform to the last qubit also, but this would not affect the |
| 102 | // final outcome. |
| 103 | // We use a within-apply block to ensure that the Hadamard transform |
| 104 | // is correctly inverted. |
| 105 | ApplyToEachA(H, queryRegister); |
| 106 | } apply { |
| 107 | H(target); |
| 108 | // We now apply Uf to the n+1 qubits, computing |
| 109 | // |x, yβͺ β¦ |x, y β f(x)βͺ. |
| 110 | Uf(queryRegister, target); |
| 111 | } |
| 112 | |
| 113 | // Measure all qubits and reset them to the |0βͺ state so that they can |
| 114 | // be safely deallocated at the end of the block. |
| 115 | let resultArray = MResetEachZ(queryRegister); |
| 116 | |
| 117 | // Finally, the last qubit, which held the y-register, is reset. |
| 118 | Reset(target); |
| 119 | |
| 120 | // The result is already contained in resultArray so no further |
| 121 | // post-processing is necessary. |
| 122 | return resultArray; |
| 123 | } |
| 124 | |
| 125 | /// # Summary |
| 126 | /// Given an integer that can be represented as a bit string |
| 127 | /// πβ = (rβ, β¦, rβββ), this operation applies a unitary π that acts on π + 1 |
| 128 | /// qubits as: |
| 129 | /// π |π₯βͺ|π¦βͺ = |π₯βͺ|π¦ β π(π₯)βͺ |
| 130 | /// where π(π₯) = Ξ£α΅’ π₯α΅’ πα΅’ mod 2. |
| 131 | /// |
| 132 | /// # Input |
| 133 | /// ## bitStringAsInt |
| 134 | /// An integer that can be represented as a bit string πβ used to define the |
| 135 | /// function π. |
| 136 | /// ## xRegister |
| 137 | /// Represents the |π₯βͺ register that π acts on. |
| 138 | /// ## yQubit |
| 139 | /// Represents the |π¦βͺ qubit that π acts on. |
| 140 | operation ApplyParityOperation( |
| 141 | bitStringAsInt : Int, |
| 142 | xRegister : Qubit[], |
| 143 | yQubit : Qubit |
| 144 | ) : Unit { |
| 145 | // `xRegister` muts have enough qubits to represent the integer. |
| 146 | let requiredBits = BitSizeI(bitStringAsInt); |
| 147 | let availableQubits = Length(xRegister); |
| 148 | Fact( |
| 149 | availableQubits >= requiredBits, |
| 150 | $"Integer value {bitStringAsInt} requires {requiredBits} bits to be represented but the quantum register only has {availableQubits} qubits" |
| 151 | ); |
| 152 | |
| 153 | // Apply the quantum operations that encode the bit string. |
| 154 | for index in IndexRange(xRegister) { |
| 155 | if ((bitStringAsInt &&& 2^index) != 0) { |
| 156 | CNOT(xRegister[index], yQubit); |
| 157 | } |
| 158 | } |
| 159 | } |
| 160 | |
| 161 | /// # Summary |
| 162 | /// Returns black-box operations (Qubit[], Qubit) => () of the form |
| 163 | /// U_f |π₯βͺ|π¦βͺ = |π₯βͺ|π¦ β π(π₯)βͺ. |
| 164 | /// We define π by providing the bit string πβ as an integer. |
| 165 | function EncodeIntegerAsParityOperation(bitStringAsInt : Int) : (Qubit[], Qubit) => Unit { |
| 166 | return ApplyParityOperation(bitStringAsInt, _, _); |
| 167 | } |