diff --git a/library/fixed_point/qsharp.json b/library/fixed_point/qsharp.json new file mode 100644 index 0000000000..78cdd82d68 --- /dev/null +++ b/library/fixed_point/qsharp.json @@ -0,0 +1,35 @@ +{ + "author": "Microsoft", + "license": "MIT", + "dependencies": { + "Signed": { + "github": { + "owner": "microsoft", + "repo": "qsharp", + "ref": "adcefe8", + "path": "library/signed" + } + }, + "Unstable": { + "github": { + "owner": "Microsoft", + "repo": "qsharp", + "ref": "adcefe8", + "path": "library/unstable" + } + } + }, + "files": [ + "src/Comparison.qs", + "src/Convert.qs", + "src/Facts.qs", + "src/Init.qs", + "src/Main.qs", + "src/Measurement.qs", + "src/Operations.qs", + "src/Polynomial.qs", + "src/Reciprocal.qs", + "src/Tests.qs", + "src/Types.qs" + ] +} diff --git a/library/fixed_point/src/Comparison.qs b/library/fixed_point/src/Comparison.qs new file mode 100644 index 0000000000..fded54b6f0 --- /dev/null +++ b/library/fixed_point/src/Comparison.qs @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import Types.FixedPoint; +import Facts.AssertFormatsAreIdenticalFxP; +import Signed.Comparison.CompareGTSI; + +/// # Summary +/// Compares two fixed-point numbers stored in quantum registers, and +/// controls a flip on the result. +/// +/// # Input +/// ## fp1 +/// First fixed-point number to be compared. +/// ## fp2 +/// Second fixed-point number to be compared. +/// ## result +/// Result of the comparison. Will be flipped if `fp1 > fp2`. +/// +/// # Remarks +/// The current implementation requires the two fixed-point numbers +/// to have the same point position and the same number of qubits. +operation CompareGreaterThanFxP(fp1 : FixedPoint, fp2 : FixedPoint, result : Qubit) : Unit is Adj + Ctl { + AssertFormatsAreIdenticalFxP([fp1, fp2]); + + CompareGTSI((fp1::Register), (fp2::Register), result); +} + +export CompareGreaterThanFxP; \ No newline at end of file diff --git a/library/fixed_point/src/Convert.qs b/library/fixed_point/src/Convert.qs new file mode 100644 index 0000000000..29fd3cd651 --- /dev/null +++ b/library/fixed_point/src/Convert.qs @@ -0,0 +1,95 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import Std.Convert.IntAsDouble, Std.Convert.BoolArrayAsInt; +import Std.Math.AbsD, Std.Math.Floor; +import Std.Arrays.Most, Std.Arrays.Tail; + +/// # Summary +/// Computes fixed-point approximation for a double and returns it as `Bool` array. +/// +/// # Input +/// ## integerBits +/// Assumed number of integer bits (including the sign bit). +/// ## fractionalBits +/// Assumed number of fractional bits. +/// ## value +/// Value to be approximated. +/// +/// # Example +/// Note that the first element in the Boolean array is the least-significant bit. +/// ```qsharp +/// let bits = FixedPointAsBoolArray(2, 2, 1.25); // bits = [true, false, true, false] +/// let bits = FixedPointAsBoolArray(2, 2, 1.3); // bits = [true, false, true, false], approximated +/// let bits = FixedPointAsBoolArray(2, 2, -1.75); // bits = [true, false, false, true], two's complement +/// ``` +function FixedPointAsBoolArray(integerBits : Int, fractionalBits : Int, value : Double) : Bool[] { + let numBits = integerBits + fractionalBits; + let sign = value < 0.0; + + mutable result = [false, size = numBits]; + mutable rescaledConstant = 2.0^IntAsDouble(fractionalBits) * AbsD(value) + 0.5; + mutable keepAdding = sign; + + for idx in 0..numBits - 1 { + let intConstant = Floor(rescaledConstant); + set rescaledConstant = rescaledConstant / 2.0; + mutable currentBit = (intConstant &&& 1) == (sign ? 0 | 1); + if keepAdding { + set keepAdding = currentBit; + set currentBit = not currentBit; + } + if currentBit { + set result w/= idx <- true; + } + } + + return result; +} + +/// # Summary +/// Returns the double value of a fixed-point approximation from of a `Bool` array. +/// +/// # Input +/// ## integerBits +/// Assumed number of integer bits (including the sign bit). +/// ## bits +/// Bit-string representation of approximated number. +/// +/// # Example +/// Note that the first element in the Boolean array is the least-significant bit. +/// ```qsharp +/// let value = BoolArrayAsFixedPoint(2, [true, false, true, false]); // value = 1.25 +/// let value = BoolArrayAsFixedPoint(2, [true, false, false, true]); // value = -1.75 +/// ``` + +function BoolArrayAsFixedPoint(integerBits : Int, bits : Bool[]) : Double { + let numBits = Length(bits); + let intPart = (Tail(bits) ? -(1 <<< (numBits - 1)) | 0) + BoolArrayAsInt(Most(bits)); + return IntAsDouble(intPart) / (2.0^IntAsDouble(numBits - integerBits)); +} + +/// # Summary +/// Discretizes a double value as a fixed-point approximation and returns its value as a double. +/// +/// # Input +/// ## integerBits +/// Assumed number of integer bits (including the sign bit). +/// ## fractionalBits +/// Assumed number of fractional bits. +/// ## value +/// Value to be approximated. +/// +/// # Example +/// ```qsharp +/// let value = DoubleAsFixedPoint(2, 2, 1.3); // value = 1.25 +/// let value = DoubleAsFixedPoint(2, 2, 0.8); // value = 0.75 +/// ``` +function DoubleAsFixedPoint(integerBits : Int, fractionalBits : Int, value : Double) : Double { + return BoolArrayAsFixedPoint(integerBits, FixedPointAsBoolArray(integerBits, fractionalBits, value)); +} + +export + FixedPointAsBoolArray, + BoolArrayAsFixedPoint, + DoubleAsFixedPoint; \ No newline at end of file diff --git a/library/fixed_point/src/Facts.qs b/library/fixed_point/src/Facts.qs new file mode 100644 index 0000000000..3f01db2868 --- /dev/null +++ b/library/fixed_point/src/Facts.qs @@ -0,0 +1,62 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import Types.FixedPoint; +import Std.Arrays.IsEmpty, Std.Arrays.Rest; +import Std.Diagnostics.Fact, Std.Diagnostics.CheckAllZero; + +/// # Summary +/// Asserts that a quantum fixed-point number is +/// initialized to zero. +/// +/// # Description +/// This assertion succeeds when all qubits are in state $\ket{0}$, +/// representing that the register encodes the fixed-point number $0.0$. +operation AssertAllZeroFxP(fp : FixedPoint) : Unit { + Fact(CheckAllZero(fp::Register), "Quantum fixed-point number was not zero."); +} + +/// # Summary +/// Assert that all fixed-point numbers in the provided array +/// have identical point positions and qubit numbers. +/// +/// # Input +/// ## fixedPoints +/// Array of quantum fixed-point numbers that will be checked for +/// compatibility (using assertions). +function AssertFormatsAreIdenticalFxP(fixedPoints : FixedPoint[]) : Unit { + if IsEmpty(fixedPoints) { + return (); + } + let (position, register) = fixedPoints[0]!; + Fact(position > 0, "Point position must be greater than zero."); + let n = Length(register); + for fp in Rest(fixedPoints) { + Fact(fp::IntegerBits == position, "FixedPoint numbers must have identical binary point position."); + Fact(Length(fp::Register) == n, "FixedPoint numbers must have identical number of qubits."); + } +} + +/// # Summary +/// Assert that all fixed-point numbers in the provided array +/// have identical point positions when counting from the least- +/// significant bit. I.e., number of bits minus point position must +/// be constant for all fixed-point numbers in the array. +/// +/// # Input +/// ## fixedPoints +/// Array of quantum fixed-point numbers that will be checked for +/// compatibility (using assertions). +function AssertPointPositionsIdenticalFxP(fixedPoints : FixedPoint[]) : Unit { + if IsEmpty(fixedPoints) { + return (); + } + let (position, register) = fixedPoints[0]!; + Fact(position > 0, "Point position must be greater than zero."); + let n = Length(register); + for fp in Rest(fixedPoints) { + Fact((Length(fp::Register) - fp::IntegerBits) == (n - position), "FixedPoint numbers must have identical point alignment."); + } +} + +export AssertAllZeroFxP, AssertFormatsAreIdenticalFxP, AssertPointPositionsIdenticalFxP; \ No newline at end of file diff --git a/library/fixed_point/src/Init.qs b/library/fixed_point/src/Init.qs new file mode 100644 index 0000000000..979dfff2c6 --- /dev/null +++ b/library/fixed_point/src/Init.qs @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import Types.FixedPoint; +import Convert.FixedPointAsBoolArray; + +/// # Summary +/// Initialize a quantum fixed-point number to a classical constant. +/// +/// # Input +/// ## constant +/// Constant to which to initialize the quantum fixed-point number. +/// ## fp +/// Fixed-point number (of type FixedPoint) to initialize. +operation PrepareFxP(constant : Double, fp : FixedPoint) : Unit is Adj + Ctl { + let bits = FixedPointAsBoolArray(fp::IntegerBits, Length(fp::Register) - fp::IntegerBits, constant); + ApplyPauliFromBitString(PauliX, true, bits, fp::Register); +} diff --git a/library/fixed_point/src/Main.qs b/library/fixed_point/src/Main.qs new file mode 100644 index 0000000000..b58df7de2a --- /dev/null +++ b/library/fixed_point/src/Main.qs @@ -0,0 +1,4 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +export Init.PrepareFxP, Measurement.MeasureFxP, Reciprocal.ComputeReciprocalFxP, Types.FixedPoint; \ No newline at end of file diff --git a/library/fixed_point/src/Measurement.qs b/library/fixed_point/src/Measurement.qs new file mode 100644 index 0000000000..1e1c42f436 --- /dev/null +++ b/library/fixed_point/src/Measurement.qs @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import Types.FixedPoint; +import Convert.BoolArrayAsFixedPoint; +import Std.Arrays.ForEach; +import Std.Convert.ResultArrayAsBoolArray; + + +/// # Summary +/// Measure a fixed-point number, returns its value as Double, and resets +/// all the register to zero. +/// +/// # Input +/// ## fp +/// Fixed-point number to measure. +operation MeasureFxP(fp : FixedPoint) : Double { + let measurements = MResetEachZ(fp::Register); + let bits = ResultArrayAsBoolArray(measurements); + return BoolArrayAsFixedPoint(fp::IntegerBits, bits); +} diff --git a/library/fixed_point/src/Operations.qs b/library/fixed_point/src/Operations.qs new file mode 100644 index 0000000000..436b7625d2 --- /dev/null +++ b/library/fixed_point/src/Operations.qs @@ -0,0 +1,161 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import Types.FixedPoint; +import Init.PrepareFxP; +import Facts.AssertPointPositionsIdenticalFxP, Facts.AssertFormatsAreIdenticalFxP, Facts.AssertAllZeroFxP; +import Signed.Operations.Invert2sSI, Signed.Operations.MultiplySI, Signed.Operations.SquareSI; +import Std.Arrays.Zipped; +import Unstable.Arithmetic.RippleCarryTTKIncByLE; + +/// # Summary +/// Adds a classical constant to a quantum fixed-point number. +/// +/// # Input +/// ## constant +/// Constant to add to the quantum fixed-point number. +/// ## fp +/// Fixed-point number to which the constant will +/// be added. +operation AddConstantFxP(constant : Double, fp : FixedPoint) : Unit is Adj + Ctl { + let n = Length(fp::Register); + use ys = Qubit[n]; + let tmpFp = FixedPoint(fp::IntegerBits, ys); + within { + PrepareFxP(constant, tmpFp); + } apply { + AddFxP(tmpFp, fp); + } +} + +/// # Summary +/// Adds two fixed-point numbers stored in quantum registers. +/// +/// # Description +/// Given two fixed-point registers respectively in states $\ket{f_1}$ and $\ket{f_2}$, +/// performs the operation $\ket{f_1} \ket{f_2} \mapsto \ket{f_1} \ket{f_1 + f_2}$. +/// +/// # Input +/// ## fp1 +/// First fixed-point number +/// ## fp2 +/// Second fixed-point number, will be updated to contain the sum of the +/// two inputs. +/// +/// # Remarks +/// The current implementation requires the two fixed-point numbers +/// to have the same point position counting from the least-significant +/// bit, i.e., $n_i$ and $p_i$ must be equal. +operation AddFxP(fp1 : FixedPoint, fp2 : FixedPoint) : Unit is Adj + Ctl { + AssertPointPositionsIdenticalFxP([fp1, fp2]); + + RippleCarryTTKIncByLE(fp1::Register, fp2::Register); +} + +/// # Summary +/// Computes the additive inverse of `fp`. +/// +/// # Input +/// ## fp +/// Fixed-point number to invert. +/// +/// # Remarks +/// Numerical inaccuracies may occur depending on the +/// bit-precision of the fixed-point number. +operation InvertFxP(fp : FixedPoint) : Unit is Adj + Ctl { + let (_, reg) = fp!; + Invert2sSI(reg); +} + +/// # Summary +/// Computes `minuend - subtrahend` and stores the difference in `minuend`. +/// +/// # Input +/// ## subtrahend +/// The subtrahend of the subtraction - the number to be subtracted. +/// ## minuend +/// The minuend of the subtraction - the number from which the other is subtracted. +/// +/// # Remarks +/// Computes the difference by inverting `subtrahend` before and after adding +/// it to `minuend`. Notice that `minuend`, the first argument is updated. +operation SubtractFxP(minuend : FixedPoint, subtrahend : FixedPoint) : Unit is Adj + Ctl { + within { + InvertFxP(subtrahend); + } apply { + AddFxP(subtrahend, minuend); + } +} + + +/// # Summary +/// Multiplies two fixed-point numbers in quantum registers. +/// +/// # Input +/// ## fp1 +/// First fixed-point number. +/// ## fp2 +/// Second fixed-point number. +/// ## result +/// Result fixed-point number, must be in state $\ket{0}$ initially. +/// +/// # Remarks +/// The current implementation requires the three fixed-point numbers +/// to have the same point position and the same number of qubits. +operation MultiplyFxP(fp1 : FixedPoint, fp2 : FixedPoint, result : FixedPoint) : Unit is Adj { + + body (...) { + Controlled MultiplyFxP([], (fp1, fp2, result)); + } + controlled (controls, ...) { + AssertFormatsAreIdenticalFxP([fp1, fp2, result]); + let n = Length(fp1::Register); + + use tmpResult = Qubit[2 * n]; + let xsInt = ((fp1::Register)); + let ysInt = ((fp2::Register)); + let tmpResultInt = tmpResult; + + within { + MultiplySI(xsInt, ysInt, tmpResultInt); + } apply { + Controlled ApplyToEachCA(controls, (CNOT, Zipped(tmpResult[n - fp1::IntegerBits..2 * n - fp1::IntegerBits - 1], result::Register))); + } + } +} + +/// # Summary +/// Squares a fixed-point number. +/// +/// # Input +/// ## fp +/// Fixed-point number. +/// ## result +/// Result fixed-point number, +/// must be in state $\ket{0}$ initially. +operation SquareFxP(fp : FixedPoint, result : FixedPoint) : Unit is Adj { + body (...) { + Controlled SquareFxP([], (fp, result)); + } + controlled (controls, ...) { + AssertFormatsAreIdenticalFxP([fp, result]); + let n = Length(fp::Register); + + use tmpResult = Qubit[2 * n]; + let xsInt = fp::Register; + let tmpResultInt = tmpResult; + within { + SquareSI(xsInt, tmpResultInt); + } apply { + Controlled ApplyToEachCA(controls, (CNOT, Zipped(tmpResult[n - fp::IntegerBits..2 * n - fp::IntegerBits - 1], result::Register))); + } + } +} + +export + AddConstantFxP, + AddFxP, + InvertFxP, + SubtractFxP, + MultiplyFxP, + SquareFxP; \ No newline at end of file diff --git a/library/fixed_point/src/Polynomial.qs b/library/fixed_point/src/Polynomial.qs new file mode 100644 index 0000000000..083c4848e1 --- /dev/null +++ b/library/fixed_point/src/Polynomial.qs @@ -0,0 +1,130 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import Facts.AssertFormatsAreIdenticalFxP, Facts.AssertAllZeroFxP; +import Types.FixedPoint; +import Init.PrepareFxP; +import Operations.MultiplyFxP, Operations.SquareFxP, Operations.AddConstantFxP; + +/// # Summary +/// Evaluates a polynomial in a fixed-point representation. +/// +/// # Input +/// ## coefficients +/// Coefficients of the polynomial as a double array, i.e., the array +/// $[a_0, a_1, ..., a_d]$ for the polynomial +/// $P(x) = a_0 + a_1 x + \cdots + a_d x^d$. +/// ## fpx +/// Input fixed-point number for which to evaluate the polynomial. +/// ## result +/// Output fixed-point number which will hold $P(x)$. Must be in state +/// $\ket{0}$ initially. +operation EvaluatePolynomialFxP(coefficients : Double[], fpx : FixedPoint, result : FixedPoint) : Unit is Adj { + body (...) { + Controlled EvaluatePolynomialFxP([], (coefficients, fpx, result)); + } + controlled (controls, ...) { + AssertFormatsAreIdenticalFxP([fpx, result]); + let degree = Length(coefficients) - 1; + let p = fpx::IntegerBits; + let n = Length(fpx::Register); + if degree == 0 { + Controlled PrepareFxP(controls, (coefficients[0], result)); + } elif degree > 0 { + // initialize ancillary register to a_d + use qubits = Qubit[n * degree]; + within { + let firstIterate = FixedPoint(p, qubits[(degree - 1) * n..degree * n - 1]); + PrepareFxP(coefficients[degree], firstIterate); + for d in degree..(-1)..2 { + let currentIterate = FixedPoint(p, qubits[(d - 1) * n..d * n - 1]); + let nextIterate = FixedPoint(p, qubits[(d - 2) * n..(d - 1) * n - 1]); + // multiply by x and then add current coefficient + MultiplyFxP(currentIterate, fpx, nextIterate); + AddConstantFxP(coefficients[d-1], nextIterate); + } + } apply { + let finalIterate = FixedPoint(p, qubits[0..n-1]); + // final multiplication into the result register + Controlled MultiplyFxP(controls, (finalIterate, fpx, result)); + // add a_0 to complete polynomial evaluation and + Controlled AddConstantFxP(controls, (coefficients[0], result)); + } + } + } +} + +/// # Summary +/// Evaluates an even polynomial in a fixed-point representation. +/// +/// # Input +/// ## coefficients +/// Coefficients of the even polynomial as a double array, i.e., the array +/// $[a_0, a_1, ..., a_k]$ for the even polynomial +/// $P(x) = a_0 + a_1 x^2 + \cdots + a_k x^{2k}$. +/// ## fpx +/// Input fixed-point number for which to evaluate the polynomial. +/// ## result +/// Output fixed-point number which will hold $P(x)$. Must be in state +/// $\ket{0}$ initially. +operation EvaluateEvenPolynomialFxP(coefficients : Double[], fpx : FixedPoint, result : FixedPoint) : Unit is Adj { + body (...) { + Controlled EvaluateEvenPolynomialFxP([], (coefficients, fpx, result)); + } + controlled (controls, ...) { + AssertFormatsAreIdenticalFxP([fpx, result]); + let halfDegree = Length(coefficients) - 1; + let n = Length(fpx::Register); + + if halfDegree == 0 { + Controlled PrepareFxP(controls, (coefficients[0], result)); + } elif halfDegree > 0 { + // initialize auxiliary register to a_d + use xsSquared = Qubit[n]; + let fpxSquared = FixedPoint(fpx::IntegerBits, xsSquared); + within { + SquareFxP(fpx, fpxSquared); + } apply { + Controlled EvaluatePolynomialFxP(controls, (coefficients, fpxSquared, result)); + } + } + } +} + +/// # Summary +/// Evaluates an odd polynomial in a fixed-point representation. +/// +/// # Input +/// ## coefficients +/// Coefficients of the odd polynomial as a double array, i.e., the array +/// $[a_0, a_1, ..., a_k]$ for the odd polynomial +/// $P(x) = a_0 x + a_1 x^3 + \cdots + a_k x^{2k+1}$. +/// ## fpx +/// Input fixed-point number for which to evaluate the polynomial. +/// ## result +/// Output fixed-point number which will hold P(x). Must be in state +/// $\ket{0}$ initially. +operation EvaluateOddPolynomialFxP(coefficients : Double[], fpx : FixedPoint, result : FixedPoint) : Unit is Adj { + body (...) { + Controlled EvaluateOddPolynomialFxP([], (coefficients, fpx, result)); + } + controlled (controls, ...) { + AssertFormatsAreIdenticalFxP([fpx, result]); + let halfDegree = Length(coefficients) - 1; + let n = Length(fpx::Register); + if halfDegree >= 0 { + use tmpResult = Qubit[n]; + let tmpResultFp = FixedPoint(fpx::IntegerBits, tmpResult); + within { + EvaluateEvenPolynomialFxP(coefficients, fpx, tmpResultFp); + } apply { + Controlled MultiplyFxP(controls, (fpx, tmpResultFp, result)); + } + } + } +} + +export + EvaluatePolynomialFxP, + EvaluateEvenPolynomialFxP, + EvaluateOddPolynomialFxP; \ No newline at end of file diff --git a/library/fixed_point/src/Reciprocal.qs b/library/fixed_point/src/Reciprocal.qs new file mode 100644 index 0000000000..4c5b4049bf --- /dev/null +++ b/library/fixed_point/src/Reciprocal.qs @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import Types.FixedPoint; +import Signed.Operations.ComputeReciprocalI, Signed.Operations.Invert2sSI; +import Std.Diagnostics.Fact; +import Std.Arrays.Tail, Std.Arrays.Zipped; +import Std.Math.Min; + +/// # Summary +/// Computes the reciprocal of a number stored in a quantum register with +/// the fixed-point representation. +/// +/// # Description +/// Given a register in the state $\ket{x}$ for a fixed-point number $x$, +/// computes the reciprocal $1 / x$ into the state of the `result` +/// register. +/// +/// # Input +/// ## x +/// Fixed-point number to be inverted. +/// ## result +/// Fixed-point number that will hold the result. Must be initialized to $\ket{0.0}$. +operation ComputeReciprocalFxP(x : FixedPoint, result : FixedPoint) : Unit is Adj { + body (...) { + Controlled ComputeReciprocalFxP([], (x, result)); + } + controlled (controls, ...) { + let (p, xs) = x!; + let (pRes, rs) = result!; + let n = Length(xs); + + Fact(p + pRes - 1 + n >= Length(rs), "Output register is too wide."); + use sign = Qubit(); + use tmpRes = Qubit[2 * n]; + CNOT(Tail(xs), sign); + (Controlled Invert2sSI)([sign], xs); + ComputeReciprocalI(xs, tmpRes); + (Controlled ApplyToEachCA)(controls, (CNOT, Zipped(tmpRes[p + pRes-1 + n-Length(rs)..Min([n + p + pRes, 2 * n-1])], rs))); + (Controlled Invert2sSI)([sign], ((rs))); + (Adjoint ComputeReciprocalI)(xs, tmpRes); + (Controlled Adjoint Invert2sSI)([sign], (xs)); + CNOT(Tail(xs), sign); + } +} + diff --git a/library/fixed_point/src/Tests.qs b/library/fixed_point/src/Tests.qs new file mode 100644 index 0000000000..24826d0765 --- /dev/null +++ b/library/fixed_point/src/Tests.qs @@ -0,0 +1,115 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import Init.PrepareFxP; +import Types.FixedPoint; +import Measurement.MeasureFxP; +import Std.Diagnostics.Fact; +import Std.Convert.IntAsDouble; +import Std.Math.AbsD; +import Operations.*; + +operation Main() : Unit { + FxpMeasurementTest(); + FxpOperationTests(); +} + +operation FxpMeasurementTest() : Unit { + for numQubits in 3..12 { + for numIntBits in 2..numQubits { + // the allowable precision should be 2.0 ^ (-numFracBits) + let numFracBits = numQubits - numIntBits; + let epsilon = 2.0^(- IntAsDouble(numFracBits)); + let numTestCases = (2^(numIntBits - 1)) + 1; + + Message($"Qubits: {numQubits}, Int bits: {numIntBits}, Frac bits: {numFracBits}, Epsilon: {epsilon}. {numTestCases} test cases"); + // only test up to the capacity of the number, which is (2 ^ (numIntBits)) + 1 (for the frac part) + for testCase in 0..numTestCases { + let constant = (IntAsDouble(numTestCases) / 2.0) - epsilon * IntAsDouble(testCase); + TestConstantMeasurement(constant, numQubits, numIntBits, epsilon); + } + Message("Passed"); + } + } +} + +operation TestConstantMeasurement(constant : Double, registerWidth : Int, integerWidth : Int, epsilon : Double) : Unit { + use register = Qubit[registerWidth]; + let newFxp = new FixedPoint { IntegerBits = integerWidth, Register = register }; + PrepareFxP(constant, newFxp); + let measured = MeasureFxP(newFxp); + let difference = AbsD(constant - measured); + Fact(difference < epsilon, $"Difference of {difference} is outside tolerance of {epsilon}. Input was {constant} and measured result was {measured}"); + ResetAll(register); +} + +operation FxpOperationTests() : Unit { + for i in 0..10 { + let constant1 = 0.2 * IntAsDouble(i); + let constant2 = 0.2 * IntAsDouble(100 - i); + TestOperation(constant1, constant2, AddFxP, (a, b) -> a + b, "Add"); + TestOperation(constant1, constant2, SubtractFxP, (a, b) -> a - b, "Subtract"); + TestOperation3(constant1, constant2, (a, b, c) => MultiplyFxP(a, b, c), (a, b) -> a * b, "Multiply"); + // manually test square, since it requires higher precision to test well + TestSquare(constant1); + } +} +operation TestSquare(a : Double) : Unit { + Message($"Testing Square({a})"); + use resultRegister = Qubit[30]; + let resultFxp = new FixedPoint { IntegerBits = 8, Register = resultRegister }; + PrepareFxP(0.0, resultFxp); + + use aRegister = Qubit[30]; + let aFxp = new FixedPoint { IntegerBits = 8, Register = aRegister }; + PrepareFxP(a, aFxp); + + SquareFxP(aFxp, resultFxp); + let measured = MeasureFxP(resultFxp); + Fact(AbsD(a * a - measured) < 0.001, $"Difference of {AbsD(a * a - measured)} is outside of the expected range. Expected {a * a} and measured result was {measured}. (Inputs were Square({a})"); + ResetAll(resultRegister); + ResetAll(aRegister); +} + +// assume the second register that `op` takes is the result register +operation TestOperation(a : Double, b : Double, op : (FixedPoint, FixedPoint) => (), reference : (Double, Double) -> Double, name : String) : Unit { + Message($"Testing {name}({a}, {b})"); + use register1 = Qubit[20]; + let aFxp = new FixedPoint { IntegerBits = 8, Register = register1 }; + PrepareFxP(a, aFxp); + + use register2 = Qubit[20]; + let bFxp = new FixedPoint { IntegerBits = 8, Register = register2 }; + PrepareFxP(b, bFxp); + + op(aFxp, bFxp); + let measured = MeasureFxP(bFxp); + + let expected = reference(a, b); + let difference = expected - measured; + Fact(difference < 0.001, $"Difference of {difference} is outside of the expected range. Expected {expected} and measured result was {measured}. (Inputs were {name}({a}, {b})"); + ResetAll(register1 + register2); +} + +// assume the third register that `op` takes is the result register +operation TestOperation3(a : Double, b : Double, op : (FixedPoint, FixedPoint, FixedPoint) => (), reference : (Double, Double) -> Double, name : String) : Unit { + Message($"Testing {name}({a}, {b})"); + use register1 = Qubit[24]; + let aFxp = new FixedPoint { IntegerBits = 8, Register = register1 }; + PrepareFxP(a, aFxp); + + use register2 = Qubit[24]; + let bFxp = new FixedPoint { IntegerBits = 8, Register = register2 }; + PrepareFxP(b, bFxp); + + use resultRegister = Qubit[24]; + let result = new FixedPoint { IntegerBits = 8, Register = resultRegister }; + + op(aFxp, bFxp, result); + let measured = MeasureFxP(result); + + let expected = reference(a, b); + let difference = expected - measured; + Fact(difference < 0.001, $"Difference of {difference} is outside of the expected range. Expected {expected} and measured result was {measured}. (Inputs were {name}({a}, {b})"); + ResetAll(register1 + register2); +} \ No newline at end of file diff --git a/library/fixed_point/src/Types.qs b/library/fixed_point/src/Types.qs new file mode 100644 index 0000000000..4c585ef26a --- /dev/null +++ b/library/fixed_point/src/Types.qs @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +/// # Summary +/// Represents a register of qubits encoding a fixed-point number. Consists of an integer that is equal to the number of +/// qubits to the left of the binary point, i.e., qubits of weight greater +/// than or equal to 1, and a quantum register. +/// There should always be at least 2 integer bits in the fixed-point number. The first one represents the sign, the second one is the most significant bit of the int. +/// There must be at least one bit left for the fixed point, so a minimum of 3 qubits are required total for this representation. +struct FixedPoint { IntegerBits : Int, Register : Qubit[] }