Skip to content
This repository was archived by the owner on Jan 12, 2024. It is now read-only.

Commit 1ac2e3c

Browse files
swernlikuzminrobin
andauthored
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 <[email protected]> * Check length of array * Fix two's complement handling * Simplify bounds check Co-authored-by: Robin Kuzmin <[email protected]>
1 parent d106fba commit 1ac2e3c

File tree

5 files changed

+85
-117
lines changed

5 files changed

+85
-117
lines changed

src/Simulation/QSharpFoundation/Convert/Convert.cs

Lines changed: 0 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -26,94 +26,6 @@ public Native(IOperationFactory m) : base(m) { }
2626
}
2727

2828

29-
public partial class MaybeBigIntAsInt
30-
{
31-
public class Native : MaybeBigIntAsInt
32-
{
33-
static (long, bool) MaybeBigIntAsIntFunc(BigInteger x)
34-
{
35-
if (x > long.MaxValue || x < long.MinValue)
36-
{
37-
return (0, false);
38-
}
39-
else
40-
{
41-
return ((long)x, true);
42-
}
43-
}
44-
45-
public Native(IOperationFactory m) : base(m) { }
46-
public override Func<BigInteger, (long, bool)> __Body__ => MaybeBigIntAsIntFunc;
47-
}
48-
}
49-
50-
51-
public partial class BigIntAsBoolArray
52-
{
53-
public class Native : BigIntAsBoolArray
54-
{
55-
static IQArray<bool> BigIntAsBoolArrayFunc(BigInteger x)
56-
{
57-
var bytes = x.ToByteArray();
58-
long n = bytes.LongLength * 8;
59-
var array = new bool[n];
60-
for (int i = 0; i < bytes.Length; i++)
61-
{
62-
var b = bytes[i];
63-
array[(8 * i) + 0] = (b & 0x01) != 0;
64-
array[(8 * i) + 1] = (b & 0x02) != 0;
65-
array[(8 * i) + 2] = (b & 0x04) != 0;
66-
array[(8 * i) + 3] = (b & 0x08) != 0;
67-
array[(8 * i) + 4] = (b & 0x10) != 0;
68-
array[(8 * i) + 5] = (b & 0x20) != 0;
69-
array[(8 * i) + 6] = (b & 0x40) != 0;
70-
array[(8 * i) + 7] = (b & 0x80) != 0;
71-
}
72-
return new QArray<bool>(array);
73-
}
74-
75-
public Native(IOperationFactory m) : base(m) { }
76-
public override Func<BigInteger, IQArray<bool>> __Body__ => BigIntAsBoolArrayFunc;
77-
}
78-
}
79-
80-
public partial class BoolArrayAsBigInt
81-
{
82-
public class Native : BoolArrayAsBigInt
83-
{
84-
static BigInteger BoolArrayAsBigIntFunc(IQArray<bool> x)
85-
{
86-
long fullBytes = x.Length / 8;
87-
long leftOver = x.Length % 8;
88-
long totalBytes = fullBytes + (leftOver > 0 ? 1 : 0);
89-
var array = new byte[totalBytes];
90-
for (int i = 0; i < fullBytes; i++)
91-
{
92-
array[i] += (byte)(x[(8 * i) + 0] ? 0x01 : 0);
93-
array[i] += (byte)(x[(8 * i) + 1] ? 0x02 : 0);
94-
array[i] += (byte)(x[(8 * i) + 2] ? 0x04 : 0);
95-
array[i] += (byte)(x[(8 * i) + 3] ? 0x08 : 0);
96-
array[i] += (byte)(x[(8 * i) + 4] ? 0x10 : 0);
97-
array[i] += (byte)(x[(8 * i) + 5] ? 0x20 : 0);
98-
array[i] += (byte)(x[(8 * i) + 6] ? 0x40 : 0);
99-
array[i] += (byte)(x[(8 * i) + 7] ? 0x80 : 0);
100-
}
101-
long last = fullBytes * 8;
102-
if (leftOver > 0) array[fullBytes] += (byte)(x[last + 0] ? 0x01 : 0);
103-
if (leftOver > 1) array[fullBytes] += (byte)(x[last + 1] ? 0x02 : 0);
104-
if (leftOver > 2) array[fullBytes] += (byte)(x[last + 2] ? 0x04 : 0);
105-
if (leftOver > 3) array[fullBytes] += (byte)(x[last + 3] ? 0x08 : 0);
106-
if (leftOver > 4) array[fullBytes] += (byte)(x[last + 4] ? 0x10 : 0);
107-
if (leftOver > 5) array[fullBytes] += (byte)(x[last + 5] ? 0x20 : 0);
108-
if (leftOver > 6) array[fullBytes] += (byte)(x[last + 6] ? 0x40 : 0);
109-
return new BigInteger(array);
110-
}
111-
112-
public Native(IOperationFactory m) : base(m) { }
113-
public override Func<IQArray<bool>, BigInteger> __Body__ => BoolArrayAsBigIntFunc;
114-
}
115-
}
116-
11729
public partial class BoolAsString
11830
{
11931
public class Native : BoolAsString

src/Simulation/QSharpFoundation/Convert/Convert.qs

Lines changed: 80 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -63,29 +63,76 @@ namespace Microsoft.Quantum.Convert {
6363
/// Converts a given big integer to an equivalent integer, if possible.
6464
/// The function returns a pair of the resulting integer and a Boolean flag
6565
/// which is true, if and only if the conversion was possible.
66-
/// # Remarks
67-
/// 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.
6866
function MaybeBigIntAsInt(a : BigInt) : (Int, Bool) {
69-
body intrinsic;
67+
if a > (1L <<< 63) - 1L or a < (-1L * (1L <<< 63)) {
68+
return (0, false);
69+
}
70+
let arr = BigIntAsBoolArray(a);
71+
let len = Length(arr);
72+
73+
// BigIntAsBoolArray always returns padded results with minimum length 8, so the below
74+
// logic can assume the last entry is the sign bit for two's complement.
75+
mutable val = 0;
76+
for i in 0..(len - 2) {
77+
set val += arr[i] ? 2 ^ i | 0;
78+
}
79+
if arr[len - 1] {
80+
set val -= 2 ^ (len - 1);
81+
}
82+
83+
return (val, true);
7084
}
7185

7286

7387
/// # Summary
7488
/// Converts a given big integer to an array of Booleans.
7589
/// The 0 element of the array is the least significant bit of the big integer.
76-
/// # Remarks
77-
/// 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.
7890
function BigIntAsBoolArray(a : BigInt) : Bool[] {
79-
body intrinsic;
91+
// To use two's complement, little endian representation of the integer, we fisrt need to track if the input
92+
// is a negative number. If so, flip it back to positive and start tracking a carry bit.
93+
let isNegative = a < 0L;
94+
mutable carry = isNegative;
95+
mutable val = isNegative ? -a | a;
96+
97+
mutable arr = [];
98+
while val != 0L {
99+
let newBit = val % 2L == 1L;
100+
if isNegative {
101+
// For negative numbers we must invert the calculated bit, so treat "true" as "0"
102+
// and "false" as "1". This means when the carry bit is set, we want to record the
103+
// calculated new bit and set the carry to the opposite, otherwise record the opposite
104+
// of the calculate bit.
105+
if carry {
106+
set arr += [newBit];
107+
set carry = not newBit;
108+
}
109+
else {
110+
set arr += [not newBit];
111+
}
112+
}
113+
else {
114+
// For positive numbers just accumulate the calculated bits into the array.
115+
set arr += [newBit];
116+
}
117+
118+
set val /= 2L;
119+
}
120+
121+
// Pad to the next higher byte length (size 8) if the length is not a non-zero multiple of 8 or
122+
// if the last bit does not agree with the sign bit.
123+
let len = Length(arr);
124+
if len == 0 or len % 8 != 0 or arr[len - 1] != isNegative {
125+
set arr += [isNegative, size = 8 - (len % 8)];
126+
}
127+
return arr;
80128
}
81129

82130

83131
/// # Summary
84132
/// Converts a given array of Booleans to an equivalent big integer.
85133
/// The 0 element of the array is the least significant bit of the big integer.
86134
/// # Remarks
87-
/// 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.
88-
/// Note that the Boolean array is padded of the right with `false` values to a length that is a multiple of 8,
135+
/// Note that the Boolean array is padded on the right with `false` values to a length that is a multiple of 8,
89136
/// and then treated as a little-endian notation of a positive or negative number following two's complement semantics.
90137
///
91138
/// # Example
@@ -94,7 +141,30 @@ namespace Microsoft.Quantum.Convert {
94141
/// let bi2 = BoolArrayAsBigInt([false, false, false, false, false, false, false, true]); // Not padded -> -128
95142
/// ```
96143
function BoolArrayAsBigInt(a : Bool[]) : BigInt {
97-
body intrinsic;
144+
mutable val = 0L;
145+
146+
if Length(a) > 0 {
147+
mutable arr = a;
148+
149+
if Length(arr) % 8 != 0 {
150+
// Padding is needed when the array is not evenly divisible by 8 (byte size).
151+
// Always pad with false to treat the input number as positive.
152+
set arr += [false, size = 8 - (Length(arr) % 8)];
153+
}
154+
155+
let len = Length(arr);
156+
for i in 0..(len - 2) {
157+
set val += arr[i] ? 2L ^ i | 0L;
158+
}
159+
if arr[len - 1] {
160+
// In two's complement the final bit is a sign bit, meaning it corresponds to
161+
// -1 * 2^i, where i is the index of the most significant bit in the nearest length
162+
// evenly divisible by 8.
163+
set val -= 2L ^ (len - 1);
164+
}
165+
}
166+
167+
return val;
98168
}
99169

100170

@@ -144,6 +214,7 @@ namespace Microsoft.Quantum.Convert {
144214
///
145215
/// # Remarks
146216
/// 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.
217+
@Deprecated("")
147218
function IntAsStringWithFormat(a : Int, fmt : String) : String {
148219
body intrinsic;
149220
}

src/Simulation/QSharpFoundation/Math/Math.cs

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -62,22 +62,6 @@ public Native(IOperationFactory m) : base(m) { }
6262
}
6363
}
6464

65-
public partial class DivRemL
66-
{
67-
public class Native : DivRemL
68-
{
69-
public Native(IOperationFactory m) : base(m) { }
70-
71-
private static (BigInteger, BigInteger) Impl((BigInteger, BigInteger) arg)
72-
{
73-
BigInteger rem;
74-
var div = BigInteger.DivRem(arg.Item1, arg.Item2, out rem);
75-
return (div, rem);
76-
}
77-
public override Func<(BigInteger, BigInteger), (BigInteger, BigInteger)> __Body__ => Impl;
78-
}
79-
}
80-
8165
public partial class ExpD
8266
{
8367
public class Native : ExpD

src/Simulation/QSharpFoundation/Math/Math.qs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,8 @@ namespace Microsoft.Quantum.Math {
7979

8080
/// # Summary
8181
/// Divides one BigInteger value by another, returns the result and the remainder as a tuple.
82-
///
83-
/// # Remarks
84-
/// See [System.Numerics.BigInteger.DivRem](https://docs.microsoft.com/dotnet/api/system.numerics.biginteger.divrem) for more details.
8582
function DivRemL(dividend : BigInt, divisor : BigInt) : (BigInt, BigInt) {
86-
body intrinsic;
83+
return (dividend / divisor, dividend % divisor);
8784
}
8885

8986
/// # Summary

src/Simulation/Simulators.Tests/Circuits/CoreOperations.qs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,7 +441,11 @@ namespace Microsoft.Quantum.Simulation.Simulators.Tests.Circuits {
441441
let arr2 = [true, true, true, false, true, false, false, false,
442442
true, false, false, false, false, false, false, false]; // Exactly 2 bytes
443443
AssertEqual(279L, BoolArrayAsBigInt(arr2));
444+
let arr3 = [true, true, true, false, true, false, false, false,
445+
true, false, false, false, false, false, false, true]; // Exactly 2 bytes, negative
446+
AssertEqual(-32489L, BoolArrayAsBigInt(arr3));
444447
AssertEqual(37L, BoolArrayAsBigInt(BigIntAsBoolArray(37L)));
448+
AssertEqual(-37L, BoolArrayAsBigInt(BigIntAsBoolArray(-37L)));
445449
let (div, rem) = DivRemL(16L, 5L);
446450
AssertEqual(3L, div);
447451
AssertEqual(1L, rem);

0 commit comments

Comments
 (0)