/// # Sample
/// Bernstein-Vazirani Algorithm
///
/// # Description
/// The Bernstein-Vazirani algorithm determines the value of a bit string
/// encoded in a function.
///
/// This Q# program implements the Bernstein-Vazirani algorithm.
import Std.Arrays.*;
import Std.Convert.*;
import Std.Diagnostics.*;
import Std.Math.*;
import Std.Measurement.*;
operation Main() : Int[] {
// Consider a function π(π₯β) on bitstrings π₯β = (π₯β, β¦, π₯βββ) of the form
// π(π₯β) β Ξ£α΅’ π₯α΅’ πα΅’
// where πβ = (πβ, β¦, πβββ) is an unknown bitstring that determines the
// parity of π.
// The BernsteinβVazirani algorithm allows determining π given a
// quantum operation that implements
// |π₯βͺ|π¦βͺ β¦ |π₯βͺ|π¦ β π(π₯)βͺ.
// The entry point function of this program, `Main`, shows how to use
// the `BernsteinVazirani` operation to determine the value of various
// integers whose bits describe π.
let nQubits = 10;
// Use the BernsteinβVazirani algorithm to determine the bit strings
// that various integers represent.
let integers = [127, 238, 512];
mutable decodedIntegers = [];
for integer in integers {
// Create an operation that encodes a bit string represented by an
// integer as a parity operation.
let parityOperation = EncodeIntegerAsParityOperation(integer);
// Use the parity operation as input to the Bernstein-Vazirani
// algorithm to determine the bit string.
let decodedBitString = BernsteinVazirani(parityOperation, nQubits);
let decodedInteger = ResultArrayAsInt(decodedBitString);
decodedIntegers += [decodedInteger];
}
return decodedIntegers;
}
/// # Summary
/// Validates the implementation of the Bernstein-Vazirani algorithm by
/// comparing the decoded integers to the expected values.
/// This also demonstrates how to use the `@Test` attribute to define a test operation.
@Test()
operation ValidateBernsteinVazirani() : Unit {
// To see how tests verify the behavior, try changing either the algorithm or the expected values
// in the assertion below and observe the test failure.
let integers = [127, 238, 512];
let results = Main();
Fact(results == integers, $"Decoded integers {results} do not match expected values {integers}");
}
/// # Summary
/// This operation implements the Bernstein-Vazirani quantum algorithm.
/// This algorithm computes for a given Boolean function that is promised to
/// be a parity π(π₯β, β¦, π₯βββ) = Ξ£α΅’ πα΅’ π₯α΅’ a result in the form of a bit
/// vector (πβ, β¦, πβββ) corresponding to the parity function.
/// Note that it is promised that the function is actually a parity
/// function.
///
/// # Input
/// ## Uf
/// A quantum operation that implements |π₯βͺ|π¦βͺ β¦ |π₯βͺ|π¦ β π(π₯)βͺ,
/// where π is a Boolean function that implements a parity Ξ£α΅’ πα΅’ π₯α΅’.
/// ## n
/// The number of bits in the input register |π₯βͺ.
///
/// # Output
/// An array of type `Result[]` that contains the parity πβ = (πβ, β¦, πβββ).
///
/// # See Also
/// - For details see Section 1.4.3 of Nielsen & Chuang.
///
/// # References
/// - [ *Ethan Bernstein and Umesh Vazirani*,
/// SIAM J. Comput., 26(5), 1411β1473, 1997 ]
/// (https://doi.org/10.1137/S0097539796300921)
operation BernsteinVazirani(Uf : ((Qubit[], Qubit) => Unit), n : Int) : Result[] {
// We allocate n + 1 clean qubits. Note that the function Uf is defined
// on inputs of the form (x, y), where x has n bits and y has 1 bit.
use queryRegister = Qubit[n];
use target = Qubit();
// The last qubit needs to be flipped so that the function will actually
// be computed into the phase when Uf is applied.
X(target);
within {
// Now, a Hadamard transform is applied to each of the qubits. As
// the last step before the measurement, a Hadamard transform is
// applied to all qubits except last one. We could apply the
// transform to the last qubit also, but this would not affect the
// final outcome.
// We use a within-apply block to ensure that the Hadamard transform
// is correctly inverted.
ApplyToEachA(H, queryRegister);
} apply {
H(target);
// We now apply Uf to the n+1 qubits, computing
// |x, yβͺ β¦ |x, y β f(x)βͺ.
Uf(queryRegister, target);
}
// Measure all qubits and reset them to the |0βͺ state so that they can
// be safely deallocated at the end of the block.
let resultArray = MResetEachZ(queryRegister);
// Finally, the last qubit, which held the y-register, is reset.
Reset(target);
// The result is already contained in resultArray so no further
// post-processing is necessary.
return resultArray;
}
/// # Summary
/// Given an integer that can be represented as a bit string
/// πβ = (rβ, β¦, rβββ), this operation applies a unitary π that acts on π + 1
/// qubits as:
/// π |π₯βͺ|π¦βͺ = |π₯βͺ|π¦ β π(π₯)βͺ
/// where π(π₯) = Ξ£α΅’ π₯α΅’ πα΅’ mod 2.
///
/// # Input
/// ## bitStringAsInt
/// An integer that can be represented as a bit string πβ used to define the
/// function π.
/// ## xRegister
/// Represents the |π₯βͺ register that π acts on.
/// ## yQubit
/// Represents the |π¦βͺ qubit that π acts on.
operation ApplyParityOperation(
bitStringAsInt : Int,
xRegister : Qubit[],
yQubit : Qubit
) : Unit {
// `xRegister` muts have enough qubits to represent the integer.
let requiredBits = BitSizeI(bitStringAsInt);
let availableQubits = Length(xRegister);
Fact(
availableQubits >= requiredBits,
$"Integer value {bitStringAsInt} requires {requiredBits} bits to be represented but the quantum register only has {availableQubits} qubits"
);
// Apply the quantum operations that encode the bit string.
for index in IndexRange(xRegister) {
if ((bitStringAsInt &&& 2^index) != 0) {
CNOT(xRegister[index], yQubit);
}
}
}
/// # Summary
/// Returns black-box operations (Qubit[], Qubit) => () of the form
/// U_f |π₯βͺ|π¦βͺ = |π₯βͺ|π¦ β π(π₯)βͺ.
/// We define π by providing the bit string πβ as an integer.
function EncodeIntegerAsParityOperation(bitStringAsInt : Int) : (Qubit[], Qubit) => Unit {
return ApplyParityOperation(bitStringAsInt, _, _);
}microsoft/qdk
Publicmirrored fromhttps://github.com/microsoft/qdkAvailable
samples/algorithms/BernsteinVazirani.qs
167lines Β· modepreview