Implementing Library functions in Q# (#1109)
* Implementing Library functions in Q# This change implements several Q# functions that were previously `body intrinsic` via existing Q# functionality. This improves compatibility with QIR generation and allows us to remove the corresponding manual C# implementation in favor of compiler generated ones. This will also improve compatibility of programs that use these functions with full QIR targets like resource estimation. * Update src/Simulation/QSharpFoundation/Convert/Convert.qs Co-authored-by: Robin Kuzmin <9372582+kuzminrobin@users.noreply.github.com> * Check length of array * Fix two's complement handling * Simplify bounds check Co-authored-by: Robin Kuzmin <9372582+kuzminrobin@users.noreply.github.com>
This commit is contained in:
Родитель
d106fba472
Коммит
1ac2e3cd15
|
@ -26,94 +26,6 @@ namespace Microsoft.Quantum.Convert
|
|||
}
|
||||
|
||||
|
||||
public partial class MaybeBigIntAsInt
|
||||
{
|
||||
public class Native : MaybeBigIntAsInt
|
||||
{
|
||||
static (long, bool) MaybeBigIntAsIntFunc(BigInteger x)
|
||||
{
|
||||
if (x > long.MaxValue || x < long.MinValue)
|
||||
{
|
||||
return (0, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
return ((long)x, true);
|
||||
}
|
||||
}
|
||||
|
||||
public Native(IOperationFactory m) : base(m) { }
|
||||
public override Func<BigInteger, (long, bool)> __Body__ => MaybeBigIntAsIntFunc;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public partial class BigIntAsBoolArray
|
||||
{
|
||||
public class Native : BigIntAsBoolArray
|
||||
{
|
||||
static IQArray<bool> BigIntAsBoolArrayFunc(BigInteger x)
|
||||
{
|
||||
var bytes = x.ToByteArray();
|
||||
long n = bytes.LongLength * 8;
|
||||
var array = new bool[n];
|
||||
for (int i = 0; i < bytes.Length; i++)
|
||||
{
|
||||
var b = bytes[i];
|
||||
array[(8 * i) + 0] = (b & 0x01) != 0;
|
||||
array[(8 * i) + 1] = (b & 0x02) != 0;
|
||||
array[(8 * i) + 2] = (b & 0x04) != 0;
|
||||
array[(8 * i) + 3] = (b & 0x08) != 0;
|
||||
array[(8 * i) + 4] = (b & 0x10) != 0;
|
||||
array[(8 * i) + 5] = (b & 0x20) != 0;
|
||||
array[(8 * i) + 6] = (b & 0x40) != 0;
|
||||
array[(8 * i) + 7] = (b & 0x80) != 0;
|
||||
}
|
||||
return new QArray<bool>(array);
|
||||
}
|
||||
|
||||
public Native(IOperationFactory m) : base(m) { }
|
||||
public override Func<BigInteger, IQArray<bool>> __Body__ => BigIntAsBoolArrayFunc;
|
||||
}
|
||||
}
|
||||
|
||||
public partial class BoolArrayAsBigInt
|
||||
{
|
||||
public class Native : BoolArrayAsBigInt
|
||||
{
|
||||
static BigInteger BoolArrayAsBigIntFunc(IQArray<bool> x)
|
||||
{
|
||||
long fullBytes = x.Length / 8;
|
||||
long leftOver = x.Length % 8;
|
||||
long totalBytes = fullBytes + (leftOver > 0 ? 1 : 0);
|
||||
var array = new byte[totalBytes];
|
||||
for (int i = 0; i < fullBytes; i++)
|
||||
{
|
||||
array[i] += (byte)(x[(8 * i) + 0] ? 0x01 : 0);
|
||||
array[i] += (byte)(x[(8 * i) + 1] ? 0x02 : 0);
|
||||
array[i] += (byte)(x[(8 * i) + 2] ? 0x04 : 0);
|
||||
array[i] += (byte)(x[(8 * i) + 3] ? 0x08 : 0);
|
||||
array[i] += (byte)(x[(8 * i) + 4] ? 0x10 : 0);
|
||||
array[i] += (byte)(x[(8 * i) + 5] ? 0x20 : 0);
|
||||
array[i] += (byte)(x[(8 * i) + 6] ? 0x40 : 0);
|
||||
array[i] += (byte)(x[(8 * i) + 7] ? 0x80 : 0);
|
||||
}
|
||||
long last = fullBytes * 8;
|
||||
if (leftOver > 0) array[fullBytes] += (byte)(x[last + 0] ? 0x01 : 0);
|
||||
if (leftOver > 1) array[fullBytes] += (byte)(x[last + 1] ? 0x02 : 0);
|
||||
if (leftOver > 2) array[fullBytes] += (byte)(x[last + 2] ? 0x04 : 0);
|
||||
if (leftOver > 3) array[fullBytes] += (byte)(x[last + 3] ? 0x08 : 0);
|
||||
if (leftOver > 4) array[fullBytes] += (byte)(x[last + 4] ? 0x10 : 0);
|
||||
if (leftOver > 5) array[fullBytes] += (byte)(x[last + 5] ? 0x20 : 0);
|
||||
if (leftOver > 6) array[fullBytes] += (byte)(x[last + 6] ? 0x40 : 0);
|
||||
return new BigInteger(array);
|
||||
}
|
||||
|
||||
public Native(IOperationFactory m) : base(m) { }
|
||||
public override Func<IQArray<bool>, BigInteger> __Body__ => BoolArrayAsBigIntFunc;
|
||||
}
|
||||
}
|
||||
|
||||
public partial class BoolAsString
|
||||
{
|
||||
public class Native : BoolAsString
|
||||
|
|
|
@ -63,20 +63,68 @@ namespace Microsoft.Quantum.Convert {
|
|||
/// Converts a given big integer to an equivalent integer, if possible.
|
||||
/// The function returns a pair of the resulting integer and a Boolean flag
|
||||
/// which is true, if and only if the conversion was possible.
|
||||
/// # Remarks
|
||||
/// See [C# BigInteger constructor](https://docs.microsoft.com/dotnet/api/system.numerics.biginteger.-ctor?view=netframework-4.7.2#System_Numerics_BigInteger__ctor_System_Int64_) for more details.
|
||||
function MaybeBigIntAsInt(a : BigInt) : (Int, Bool) {
|
||||
body intrinsic;
|
||||
if a > (1L <<< 63) - 1L or a < (-1L * (1L <<< 63)) {
|
||||
return (0, false);
|
||||
}
|
||||
let arr = BigIntAsBoolArray(a);
|
||||
let len = Length(arr);
|
||||
|
||||
// BigIntAsBoolArray always returns padded results with minimum length 8, so the below
|
||||
// logic can assume the last entry is the sign bit for two's complement.
|
||||
mutable val = 0;
|
||||
for i in 0..(len - 2) {
|
||||
set val += arr[i] ? 2 ^ i | 0;
|
||||
}
|
||||
if arr[len - 1] {
|
||||
set val -= 2 ^ (len - 1);
|
||||
}
|
||||
|
||||
return (val, true);
|
||||
}
|
||||
|
||||
|
||||
/// # Summary
|
||||
/// Converts a given big integer to an array of Booleans.
|
||||
/// The 0 element of the array is the least significant bit of the big integer.
|
||||
/// # Remarks
|
||||
/// See [C# BigInteger constructor](https://docs.microsoft.com/dotnet/api/system.numerics.biginteger.-ctor?view=netframework-4.7.2#System_Numerics_BigInteger__ctor_System_Int64_) for more details.
|
||||
function BigIntAsBoolArray(a : BigInt) : Bool[] {
|
||||
body intrinsic;
|
||||
// To use two's complement, little endian representation of the integer, we fisrt need to track if the input
|
||||
// is a negative number. If so, flip it back to positive and start tracking a carry bit.
|
||||
let isNegative = a < 0L;
|
||||
mutable carry = isNegative;
|
||||
mutable val = isNegative ? -a | a;
|
||||
|
||||
mutable arr = [];
|
||||
while val != 0L {
|
||||
let newBit = val % 2L == 1L;
|
||||
if isNegative {
|
||||
// For negative numbers we must invert the calculated bit, so treat "true" as "0"
|
||||
// and "false" as "1". This means when the carry bit is set, we want to record the
|
||||
// calculated new bit and set the carry to the opposite, otherwise record the opposite
|
||||
// of the calculate bit.
|
||||
if carry {
|
||||
set arr += [newBit];
|
||||
set carry = not newBit;
|
||||
}
|
||||
else {
|
||||
set arr += [not newBit];
|
||||
}
|
||||
}
|
||||
else {
|
||||
// For positive numbers just accumulate the calculated bits into the array.
|
||||
set arr += [newBit];
|
||||
}
|
||||
|
||||
set val /= 2L;
|
||||
}
|
||||
|
||||
// Pad to the next higher byte length (size 8) if the length is not a non-zero multiple of 8 or
|
||||
// if the last bit does not agree with the sign bit.
|
||||
let len = Length(arr);
|
||||
if len == 0 or len % 8 != 0 or arr[len - 1] != isNegative {
|
||||
set arr += [isNegative, size = 8 - (len % 8)];
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
|
||||
|
@ -84,8 +132,7 @@ namespace Microsoft.Quantum.Convert {
|
|||
/// Converts a given array of Booleans to an equivalent big integer.
|
||||
/// The 0 element of the array is the least significant bit of the big integer.
|
||||
/// # Remarks
|
||||
/// See [C# BigInteger constructor](https://docs.microsoft.com/dotnet/api/system.numerics.biginteger.-ctor?view=netframework-4.7.2#System_Numerics_BigInteger__ctor_System_Int64_) for more details.
|
||||
/// Note that the Boolean array is padded of the right with `false` values to a length that is a multiple of 8,
|
||||
/// Note that the Boolean array is padded on the right with `false` values to a length that is a multiple of 8,
|
||||
/// and then treated as a little-endian notation of a positive or negative number following two's complement semantics.
|
||||
///
|
||||
/// # Example
|
||||
|
@ -94,7 +141,30 @@ namespace Microsoft.Quantum.Convert {
|
|||
/// let bi2 = BoolArrayAsBigInt([false, false, false, false, false, false, false, true]); // Not padded -> -128
|
||||
/// ```
|
||||
function BoolArrayAsBigInt(a : Bool[]) : BigInt {
|
||||
body intrinsic;
|
||||
mutable val = 0L;
|
||||
|
||||
if Length(a) > 0 {
|
||||
mutable arr = a;
|
||||
|
||||
if Length(arr) % 8 != 0 {
|
||||
// Padding is needed when the array is not evenly divisible by 8 (byte size).
|
||||
// Always pad with false to treat the input number as positive.
|
||||
set arr += [false, size = 8 - (Length(arr) % 8)];
|
||||
}
|
||||
|
||||
let len = Length(arr);
|
||||
for i in 0..(len - 2) {
|
||||
set val += arr[i] ? 2L ^ i | 0L;
|
||||
}
|
||||
if arr[len - 1] {
|
||||
// In two's complement the final bit is a sign bit, meaning it corresponds to
|
||||
// -1 * 2^i, where i is the index of the most significant bit in the nearest length
|
||||
// evenly divisible by 8.
|
||||
set val -= 2L ^ (len - 1);
|
||||
}
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
|
@ -144,6 +214,7 @@ namespace Microsoft.Quantum.Convert {
|
|||
///
|
||||
/// # Remarks
|
||||
/// See [C# Int64.ToString](https://docs.microsoft.com/dotnet/api/system.int64.tostring?view=netframework-4.7.1#System_Int64_ToString_System_String_) for more details.
|
||||
@Deprecated("")
|
||||
function IntAsStringWithFormat(a : Int, fmt : String) : String {
|
||||
body intrinsic;
|
||||
}
|
||||
|
|
|
@ -62,22 +62,6 @@ namespace Microsoft.Quantum.Math
|
|||
}
|
||||
}
|
||||
|
||||
public partial class DivRemL
|
||||
{
|
||||
public class Native : DivRemL
|
||||
{
|
||||
public Native(IOperationFactory m) : base(m) { }
|
||||
|
||||
private static (BigInteger, BigInteger) Impl((BigInteger, BigInteger) arg)
|
||||
{
|
||||
BigInteger rem;
|
||||
var div = BigInteger.DivRem(arg.Item1, arg.Item2, out rem);
|
||||
return (div, rem);
|
||||
}
|
||||
public override Func<(BigInteger, BigInteger), (BigInteger, BigInteger)> __Body__ => Impl;
|
||||
}
|
||||
}
|
||||
|
||||
public partial class ExpD
|
||||
{
|
||||
public class Native : ExpD
|
||||
|
|
|
@ -79,11 +79,8 @@ namespace Microsoft.Quantum.Math {
|
|||
|
||||
/// # Summary
|
||||
/// Divides one BigInteger value by another, returns the result and the remainder as a tuple.
|
||||
///
|
||||
/// # Remarks
|
||||
/// See [System.Numerics.BigInteger.DivRem](https://docs.microsoft.com/dotnet/api/system.numerics.biginteger.divrem) for more details.
|
||||
function DivRemL(dividend : BigInt, divisor : BigInt) : (BigInt, BigInt) {
|
||||
body intrinsic;
|
||||
return (dividend / divisor, dividend % divisor);
|
||||
}
|
||||
|
||||
/// # Summary
|
||||
|
|
|
@ -441,7 +441,11 @@ namespace Microsoft.Quantum.Simulation.Simulators.Tests.Circuits {
|
|||
let arr2 = [true, true, true, false, true, false, false, false,
|
||||
true, false, false, false, false, false, false, false]; // Exactly 2 bytes
|
||||
AssertEqual(279L, BoolArrayAsBigInt(arr2));
|
||||
let arr3 = [true, true, true, false, true, false, false, false,
|
||||
true, false, false, false, false, false, false, true]; // Exactly 2 bytes, negative
|
||||
AssertEqual(-32489L, BoolArrayAsBigInt(arr3));
|
||||
AssertEqual(37L, BoolArrayAsBigInt(BigIntAsBoolArray(37L)));
|
||||
AssertEqual(-37L, BoolArrayAsBigInt(BigIntAsBoolArray(-37L)));
|
||||
let (div, rem) = DivRemL(16L, 5L);
|
||||
AssertEqual(3L, div);
|
||||
AssertEqual(1L, rem);
|
||||
|
|
Загрузка…
Ссылка в новой задаче