Skip to content

Commit

Permalink
Port fixed point library to modern QDK (#1838)
Browse files Browse the repository at this point in the history
This PR ports the classic QDK FixedPoint library to the modern QDK.
There is still some work to be done (a README, some tests), but this is
a good MVP for the library to go in 1.8.

---------

Co-authored-by: César Zaragoza Cortés <[email protected]>
  • Loading branch information
sezna and cesarzc authored Aug 29, 2024
1 parent 58a54ca commit 78ad29e
Show file tree
Hide file tree
Showing 12 changed files with 726 additions and 0 deletions.
35 changes: 35 additions & 0 deletions library/fixed_point/qsharp.json
Original file line number Diff line number Diff line change
@@ -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"
]
}
29 changes: 29 additions & 0 deletions library/fixed_point/src/Comparison.qs
Original file line number Diff line number Diff line change
@@ -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;
95 changes: 95 additions & 0 deletions library/fixed_point/src/Convert.qs
Original file line number Diff line number Diff line change
@@ -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;
62 changes: 62 additions & 0 deletions library/fixed_point/src/Facts.qs
Original file line number Diff line number Diff line change
@@ -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;
18 changes: 18 additions & 0 deletions library/fixed_point/src/Init.qs
Original file line number Diff line number Diff line change
@@ -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);
}
4 changes: 4 additions & 0 deletions library/fixed_point/src/Main.qs
Original file line number Diff line number Diff line change
@@ -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;
21 changes: 21 additions & 0 deletions library/fixed_point/src/Measurement.qs
Original file line number Diff line number Diff line change
@@ -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);
}
Loading

0 comments on commit 78ad29e

Please sign in to comment.