Add more numerics functionality (#102)

* Adding high-level integer functionality and polynomial evaluation in fixed-point.

* Restructuring Numerics.

* Adapt to naming convention.

* Add Numerics to pack step.

* Tabs to spaces.

* Primitive -> Intrinsic

* Fix naming of 2's complement inversion.

* Improved controlled poly. evaluation.

* Update Numerics/src/FixedPoint/Types.qs

* Applying first round of suggestions.

* Fixed a typo in one test.

* Last couple changes from review and discussion.
This commit is contained in:
Thomas Haener 2019-05-29 17:51:40 -07:00 коммит произвёл Andres Paz
Родитель 208b5b6bc5
Коммит 154df74705
21 изменённых файлов: 1735 добавлений и 1 удалений

Просмотреть файл

@ -12,6 +12,7 @@ steps:
projects: |
$(LibrariesRootFolder)/Standard.sln
$(LibrariesRootFolder)/Chemistry.sln
$(LibrariesRootFolder)/Numerics.sln
arguments: >
-c $(BuildConfiguration)
-v n
@ -32,6 +33,7 @@ steps:
$(LibrariesRootFolder)/Chemistry/tests/ChemistryTests/ChemistryTests.csproj
$(LibrariesRootFolder)/Chemistry/tests/SystemTests/SystemTests.csproj
$(LibrariesRootFolder)/Chemistry/tests/DataModelTests/DataModelTests.csproj
$(LibrariesRootFolder)/Numerics/tests/NumericsTests.csproj
arguments: >
-c $(BuildConfiguration)
-v n
@ -50,10 +52,10 @@ steps:
projects: |
$(LibrariesRootFolder)/Standard/src/Standard.csproj
$(LibrariesRootFolder)/Chemistry/src/DataModel/DataModel.csproj
$(LibrariesRootFolder)/Numerics/src/Numerics.csproj
arguments: >
--no-build
-c $(BuildConfiguration)
-v n
-o $(System.DefaultWorkingDirectory)
/p:PackageVersion=$(Nuget.Version)

37
Numerics.sln Normal file
Просмотреть файл

@ -0,0 +1,37 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.28307.539
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Numerics", "Numerics\src\Numerics.csproj", "{C7E6F1F9-5DB7-41D6-ADA4-3E8F5BAEF731}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NumericsTests", "Numerics\tests\NumericsTests.csproj", "{B8E99F28-CF37-4AA1-BDEC-16D3573CAEFD}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Standard", "Standard\src\Standard.csproj", "{4EE69CC7-1158-49A2-A28F-9443B06D4A32}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C7E6F1F9-5DB7-41D6-ADA4-3E8F5BAEF731}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C7E6F1F9-5DB7-41D6-ADA4-3E8F5BAEF731}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C7E6F1F9-5DB7-41D6-ADA4-3E8F5BAEF731}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C7E6F1F9-5DB7-41D6-ADA4-3E8F5BAEF731}.Release|Any CPU.Build.0 = Release|Any CPU
{B8E99F28-CF37-4AA1-BDEC-16D3573CAEFD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B8E99F28-CF37-4AA1-BDEC-16D3573CAEFD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B8E99F28-CF37-4AA1-BDEC-16D3573CAEFD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B8E99F28-CF37-4AA1-BDEC-16D3573CAEFD}.Release|Any CPU.Build.0 = Release|Any CPU
{4EE69CC7-1158-49A2-A28F-9443B06D4A32}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4EE69CC7-1158-49A2-A28F-9443B06D4A32}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4EE69CC7-1158-49A2-A28F-9443B06D4A32}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4EE69CC7-1158-49A2-A28F-9443B06D4A32}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {62FF308E-50FB-430C-B3EE-BE8E142835C9}
EndGlobalSection
EndGlobal

Просмотреть файл

@ -0,0 +1,51 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
namespace Microsoft.Quantum.Arithmetic {
open Microsoft.Quantum.Canon;
/// # 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 (px, xs) = fp!;
let n = Length(xs);
using (ys = Qubit[n]){
let tmpFp = FixedPoint(px, ys);
ApplyWithCA(PrepareFxP(constant, _), AddFxP(_, fp), tmpFp);
}
}
/// # 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 {
let (px, xs) = fp1!;
let (py, ys) = fp2!;
IdenticalPointPosFactFxP([fp1, fp2]);
AddI(LittleEndian(xs), LittleEndian(ys));
}
}

Просмотреть файл

@ -0,0 +1,31 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
namespace Microsoft.Quantum.Arithmetic {
/// # 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 {
let (px, xs) = fp1!;
let (py, ys) = fp2!;
IdenticalFormatFactFxP([fp1, fp2]);
CompareGTSI(SignedLittleEndian(LittleEndian(xs)),
SignedLittleEndian(LittleEndian(ys)),
result);
}
}

Просмотреть файл

@ -0,0 +1,63 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
namespace Microsoft.Quantum.Arithmetic {
open Microsoft.Quantum.Diagnostics;
open Microsoft.Quantum.Arrays;
/// # Summary
/// Helper function to assert that a quantum fixed-point number is
/// initialized to zero, i.e., all qubits are in state $\ket{0}$.
operation AssertAllZeroFxP(fp : FixedPoint) : Unit is Adj + Ctl {
let (p, xs) = fp!;
AssertAllZero(xs);
}
/// # 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 IdenticalFormatFactFxP(fixedPoints : FixedPoint[]) : Unit {
if (Length(fixedPoints) == 0) {
return ();
}
let (position, register) = fixedPoints[0]!;
Fact(position > 0, "Point position must be greater than zero.");
let n = Length(register);
for (fp in Most(fixedPoints)) {
let (pos, reg) = fp!;
EqualityFactI(pos, position,
"FixedPoint numbers must have identical binary point position.");
EqualityFactI(Length(reg), 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 IdenticalPointPosFactFxP(fixedPoints : FixedPoint[]) : Unit {
if (Length(fixedPoints) == 0) {
return ();
}
let (position, register) = fixedPoints[0]!;
Fact(position > 0, "Point position must be greater than zero.");
let n = Length(register);
for (fp in Most(fixedPoints)) {
let (pos, reg) = fp!;
EqualityFactI(Length(reg) - pos, n - position,
"FixedPoint numbers must have identical point alignment.");
}
}
}

Просмотреть файл

@ -0,0 +1,41 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
namespace Microsoft.Quantum.Arithmetic {
open Microsoft.Quantum.Convert;
open Microsoft.Quantum.Intrinsic;
open Microsoft.Quantum.Math;
/// # 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{
body (...) {
let (p, q) = fp!;
let n = Length(q);
let sign = constant < 0.;
mutable rescaledConstant = PowD(2., IntAsDouble(n-p)) * AbsD(constant) + 0.5;
mutable keepAdding = sign;
for (i in 0..n-1) {
let intConstant = Floor(rescaledConstant);
set rescaledConstant = 0.5 * rescaledConstant;
mutable currentBit = (intConstant &&& 1) == (sign ? 0 | 1);
if (keepAdding) {
set keepAdding = currentBit;
set currentBit = not currentBit;
}
if (currentBit) {
X(q[i]);
}
}
}
controlled auto;
adjoint self;
adjoint controlled auto;
}
}

Просмотреть файл

@ -0,0 +1,32 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
namespace Microsoft.Quantum.Arithmetic {
open Microsoft.Quantum.Convert;
open Microsoft.Quantum.Measurement;
open Microsoft.Quantum.Math;
/// # 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 (p, xs) = fp!;
let n = Length(xs);
let sign = MResetZ(xs[n-1]) == One;
mutable keepAdding = sign;
mutable fpAsDouble = 0.;
for (i in 0..n-2) {
mutable currentRes = MResetZ(xs[i]) == (sign ? Zero | One);
if (keepAdding) {
set keepAdding = currentRes;
set currentRes = not currentRes;
}
set fpAsDouble = fpAsDouble * 0.5 + (currentRes == true ? 1. | 0.);
}
return (sign ? -1.0 | 1.0) * fpAsDouble * PowD(2.0, IntAsDouble(p-2));
}
}

Просмотреть файл

@ -0,0 +1,85 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
namespace Microsoft.Quantum.Arithmetic {
open Microsoft.Quantum.Arrays;
open Microsoft.Quantum.Intrinsic;
open Microsoft.Quantum.Canon;
/// # 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) (new Qubit[0],
(fp1, fp2, result));
}
controlled (controls, ...){
IdenticalFormatFactFxP([fp1, fp2, result]);
AssertAllZeroFxP(result);
let (px, xs) = fp1!;
let (py, ys) = fp2!;
let (pz, zs) = result!;
let n = Length(xs);
using (tmpResult = Qubit[2*n]){
let xsInt = SignedLittleEndian(LittleEndian(xs));
let ysInt = SignedLittleEndian(LittleEndian(ys));
let tmpResultInt = SignedLittleEndian(
LittleEndian(tmpResult));
MultiplySI(xsInt, ysInt, tmpResultInt);
(Controlled ApplyToEachCA)(controls,
(CNOT,
Zip(tmpResult[n-px..2*n-px-1], zs)));
(Adjoint MultiplySI)(xsInt, ysInt, tmpResultInt);
}
}
}
/// # 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) (new Qubit[0],
(fp, result));
}
controlled (controls, ...){
IdenticalFormatFactFxP([fp, result]);
AssertAllZeroFxP(result);
let (px, xs) = fp!;
let (py, ys) = result!;
let n = Length(xs);
using (tmpResult = Qubit[2*n]){
let xsInt = SignedLittleEndian(LittleEndian(xs));
let tmpResultInt = SignedLittleEndian(
LittleEndian(tmpResult));
SquareSI(xsInt, tmpResultInt);
(Controlled ApplyToEachCA)(controls,
(CNOT,
Zip(tmpResult[n-px..2*n-px-1], ys)));
(Adjoint SquareSI)(xsInt, tmpResultInt);
}
}
}
}

Просмотреть файл

@ -0,0 +1,148 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
namespace Microsoft.Quantum.Arithmetic {
open Microsoft.Quantum.Canon;
/// # 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) (new Qubit[0],
(coefficients, fpx, result));
}
controlled (controls, ...) {
IdenticalFormatFactFxP([fpx, result]);
AssertAllZeroFxP(result);
let degree = Length(coefficients) - 1;
let (p, q) = fpx!;
let n = Length(q);
if (degree == 0){
(Controlled PrepareFxP)(controls,
(coefficients[0], result));
}
elif (degree > 0) {
// initialize ancillary register to a_d
using (qubits = Qubit[n * degree]){
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);
}
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));
// uncompute intermediate results
for (d in 2..degree) {
let currentIterate = FixedPoint(p, qubits[(d-1)*n..d*n-1]);
let nextIterate = FixedPoint(p, qubits[(d-2)*n..(d-1)*n-1]);
(Adjoint AddConstantFxP)(coefficients[d-1], nextIterate);
(Adjoint MultiplyFxP)(currentIterate, fpx,
nextIterate);
}
PrepareFxP(coefficients[degree], firstIterate);
}
}
}
}
/// # 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) (new Qubit[0],
(coefficients, fpx, result));
}
controlled (controls, ...) {
IdenticalFormatFactFxP([fpx, result]);
AssertAllZeroFxP(result);
let halfDegree = Length(coefficients) - 1;
let (p, q) = fpx!;
let n = Length(q);
if (halfDegree == 0){
(Controlled PrepareFxP)(controls,
(coefficients[0], result));
}
elif (halfDegree > 0) {
// initialize ancillary register to a_d
using (xsSquared = Qubit[n]){
let fpxSquared = FixedPoint(p, xsSquared);
ApplyWithCA(SquareFxP(fpx, _),
(Controlled EvaluatePolynomialFxP)(controls,
(coefficients, _, result)),
fpxSquared);
}
}
}
}
/// # 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) (new Qubit[0],
(coefficients, fpx, result));
}
controlled (controls, ...) {
IdenticalFormatFactFxP([fpx, result]);
AssertAllZeroFxP(result);
let halfDegree = Length(coefficients) - 1;
let (p, q) = fpx!;
let n = Length(q);
if (halfDegree >= 0) {
using (tmpResult = Qubit[n]) {
let tmpResultFp = FixedPoint(p, tmpResult);
ApplyWithCA(EvaluateEvenPolynomialFxP(coefficients, _, _),
(Controlled MultiplyFxP)(controls,
(_, _, result)),
(fpx, tmpResultFp));
}
}
}
}
}

Просмотреть файл

@ -0,0 +1,44 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
namespace Microsoft.Quantum.Arithmetic {
open Microsoft.Quantum.Math;
open Microsoft.Quantum.Canon;
open Microsoft.Quantum.Arrays;
open Microsoft.Quantum.Intrinsic;
open Microsoft.Quantum.Diagnostics;
/// # Summary
/// Computes $1/x$ for a fixed-point number $x$.
///
/// # 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) (new Qubit[0], (x, result));
}
controlled (controls, ...) {
let (p, xs) = x!;
let (pRes, rs) = result!;
let n = Length(xs);
AssertAllZero(rs);
Fact(p + pRes - 1 + n >= Length(rs), "Output register is too wide.");
using ((sign, tmpRes) = (Qubit(), Qubit[2*n])) {
CNOT(Tail(xs), sign);
(Controlled Invert2sSI)
([sign], SignedLittleEndian(LittleEndian(xs)));
ComputeReciprocalI(LittleEndian(xs), LittleEndian(tmpRes));
(Controlled ApplyToEachCA)(controls,
(CNOT, Zip(tmpRes[p+pRes-1+n-Length(rs)..Min([n+p+pRes, 2*n-1])], rs)));
(Controlled Invert2sSI)([sign], SignedLittleEndian(LittleEndian(rs)));
(Adjoint ComputeReciprocalI)(LittleEndian(xs), LittleEndian(tmpRes));
(Controlled Adjoint Invert2sSI)
([sign], SignedLittleEndian(LittleEndian(xs)));
CNOT(Tail(xs), sign);
}
}
}
}

Просмотреть файл

@ -0,0 +1,10 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
namespace Microsoft.Quantum.Arithmetic {
/// # 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.
newtype FixedPoint = (Int, Qubit[]);
}

Просмотреть файл

@ -0,0 +1,71 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
namespace Microsoft.Quantum.Arithmetic {
open Microsoft.Quantum.Intrinsic;
open Microsoft.Quantum.Arrays;
/// # Summary
/// Automatically chooses between addition with
/// carry and without, depending on the register size of `ys`.
///
/// # Input
/// ## xs
/// $n$-bit addend.
/// ## ys
/// Addend with at least $n$ qubits. Will hold the result.
operation AddI (xs: LittleEndian, ys: LittleEndian) : Unit is Adj + Ctl {
if (Length(xs!) == Length(ys!)) {
RippleCarryAdderNoCarryTTK(xs, ys);
}
elif (Length(ys!) > Length(xs!)) {
using (qs = Qubit[Length(ys!) - Length(xs!) - 1]){
RippleCarryAdderTTK(LittleEndian(xs! + qs),
LittleEndian(Most(ys!)), Tail(ys!));
}
}
else {
fail "xs must not contain more qubits than ys!";
}
}
/// # Summary
/// Wrapper for integer comparison: `result = x > y`.
///
/// # Input
/// ## xs
/// First $n$-bit number
/// ## ys
/// Second $n$-bit number
/// ## result
/// Will be flipped if $x > y$
operation CompareGTI (xs: LittleEndian, ys: LittleEndian,
result: Qubit) : Unit is Adj + Ctl {
GreaterThan(xs, ys, result);
}
/// # Summary
/// Wrapper for signed integer comparison: `result = xs > ys`.
///
/// # Input
/// ## xs
/// First $n$-bit number
/// ## ys
/// Second $n$-bit number
/// ## result
/// Will be flipped if $xs > ys$
operation CompareGTSI (xs: SignedLittleEndian,
ys: SignedLittleEndian,
result: Qubit) : Unit is Adj + Ctl {
using (tmp = Qubit()) {
CNOT(Tail(xs!!), tmp);
CNOT(Tail(ys!!), tmp);
X(tmp);
(Controlled CompareGTI)([tmp], (xs!, ys!, result));
X(tmp);
CCNOT(tmp, Tail(ys!!), result);
CNOT(Tail(xs!!), tmp);
CNOT(Tail(ys!!), tmp);
}
}
}

Просмотреть файл

@ -0,0 +1,56 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
namespace Microsoft.Quantum.Arithmetic {
open Microsoft.Quantum.Intrinsic;
open Microsoft.Quantum.Diagnostics;
/// # Summary
/// Divides two quantum integers.
///
/// # Description
/// `xs` will hold the
/// remainder `xs - floor(xs/ys) * ys` and `result` will hold
/// `floor(xs/ys)`.
///
/// # Input
/// ## xs
/// $n$-bit dividend, will be replaced by the remainder.
/// ## ys
/// $n$-bit divisor
/// ## result
/// $n$-bit result, must be in state $\ket{0}$ initially
/// and will be replaced by the result of the integer division.
///
/// # Remarks
/// Uses a standard shift-and-subtract approach to implement the division.
/// The controlled version is specialized such the subtraction does not
/// require additional controls.
operation DivideI (xs: LittleEndian, ys: LittleEndian,
result: LittleEndian) : Unit {
body (...) {
(Controlled DivideI) (new Qubit[0], (xs, ys, result));
}
controlled (controls, ...) {
let n = Length(result!);
EqualityFactI(n, Length(ys!), "Integer division requires
equally-sized registers ys and result.");
EqualityFactI(n, Length(xs!), "Integer division
requires an n-bit dividend registers.");
AssertAllZero(result!);
let xpadded = LittleEndian(xs! + result!);
for (i in (n-1)..(-1)..0) {
let xtrunc = LittleEndian(xpadded![i..i+n-1]);
(Controlled CompareGTI) (controls, (ys, xtrunc, result![i]));
// if ys > xtrunc, we don't subtract:
(Controlled X) (controls, result![i]);
(Controlled Adjoint AddI) ([result![i]], (ys, xtrunc));
}
}
adjoint auto;
adjoint controlled auto;
}
}

Просмотреть файл

@ -0,0 +1,75 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
namespace Microsoft.Quantum.Arithmetic {
open Microsoft.Quantum.Arrays;
open Microsoft.Quantum.Diagnostics;
open Microsoft.Quantum.Canon;
open Microsoft.Quantum.Intrinsic;
/// # Summary
/// Inverts a given integer modulo 2's complement.
///
/// # Input
/// ## xs
/// n-bit signed integer (SignedLittleEndian), will be inverted modulo
/// 2's complement.
operation Invert2sSI (xs: SignedLittleEndian) : Unit {
body (...) {
(Controlled Invert2sSI) (new Qubit[0], xs);
}
controlled (controls, ...) {
ApplyToEachCA((Controlled X)(controls, _), xs!!);
using (ancillas = Qubit[Length(xs!!)]) {
(Controlled X)(controls, ancillas[0]);
AddI(LittleEndian(ancillas), xs!);
(Controlled X)(controls, ancillas[0]);
}
}
adjoint auto;
adjoint controlled auto;
}
/// # Summary
/// Computes the reciprocal 1/x for an unsigned integer x
/// using integer division. The result, interpreted as an integer,
/// will be `floor(2^(2*n-1) / x)`.
///
/// # Input
/// ## xs
/// n-bit unsigned integer
/// ## result
/// 2n-bit output, must be in $\ket{0}$ initially.
///
/// # Remarks
/// For the input x=0, the output will be all-ones.
operation ComputeReciprocalI (xs: LittleEndian,
result: LittleEndian) : Unit {
body (...) {
(Controlled ComputeReciprocalI) (new Qubit[0], (xs, result));
}
controlled (controls, ...) {
let n = Length(xs!);
AssertIntEqual(Length(result!), 2*n,
"Result register must contain 2n qubits.");
AssertAllZero(result!);
using ((lhs, padding) = (Qubit[2*n], Qubit[n])) {
let paddedxs = LittleEndian(xs! + padding);
X(Tail(lhs)); // initialize left-hand side to 2^{2n-1}
// ... and divide:
(Controlled DivideI) (controls,
(LittleEndian(lhs), paddedxs, result));
// uncompute lhs
for (i in 0..2*n-1) {
(Controlled AddI) ([result![i]],
(LittleEndian(paddedxs![0..2*n-1-i]),
LittleEndian(lhs[i..2*n-1])));
}
X(Tail(lhs));
}
}
adjoint auto;
adjoint controlled auto;
}
}

Просмотреть файл

@ -0,0 +1,177 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
namespace Microsoft.Quantum.Arithmetic {
open Microsoft.Quantum.Arrays;
open Microsoft.Quantum.Intrinsic;
open Microsoft.Quantum.Diagnostics;
/// # Summary
/// Multiply integer `xs` by integer `ys` and store the result in `result`,
/// which must be zero initially.
///
/// # Input
/// ## xs
/// $n$-bit multiplicand (LittleEndian)
/// ## ys
/// $n$-bit multiplier (LittleEndian)
/// ## result
/// $2n$-bit result (LittleEndian), must be in state $\ket{0}$ initially.
///
/// # Remarks
/// Uses a standard shift-and-add approach to implement the multiplication.
/// The controlled version was improved by copying out $x_i$ to an ancilla
/// qubit conditioned on the control qubits, and then controlling the
/// addition on the ancilla qubit.
operation MultiplyI (xs: LittleEndian, ys: LittleEndian,
result: LittleEndian) : Unit {
body (...) {
let n = Length(xs!);
EqualityFactI(n, Length(ys!), "Integer multiplication requires
equally-sized registers xs and ys.");
EqualityFactI(2 * n, Length(result!), "Integer multiplication
requires a 2n-bit result registers.");
AssertAllZero(result!);
for (i in 0..n-1) {
(Controlled AddI) ([xs![i]], (ys, LittleEndian(result![i..i+n])));
}
}
controlled (controls, ...) {
let n = Length(xs!);
EqualityFactI(n, Length(ys!), "Integer multiplication requires
equally-sized registers xs and ys.");
EqualityFactI(2 * n, Length(result!), "Integer multiplication
requires a 2n-bit result registers.");
AssertAllZero(result!);
using (anc = Qubit()) {
for (i in 0..n-1) {
(Controlled CNOT) (controls, (xs![i], anc));
(Controlled AddI) ([anc], (ys, LittleEndian(result![i..i+n])));
(Controlled CNOT) (controls, (xs![i], anc));
}
}
}
adjoint auto;
adjoint controlled auto;
}
/// # Summary
/// Computes the square of the integer `xs` into `result`,
/// which must be zero initially.
///
/// # Input
/// ## xs
/// $n$-bit number to square (LittleEndian)
/// ## result
/// $2n$-bit result (LittleEndian), must be in state $\ket{0}$ initially.
///
/// # Remarks
/// Uses a standard shift-and-add approach to compute the square. Saves
/// $n-1$ qubits compared to the straight-forward solution which first
/// copies out xs before applying a regular multiplier and then undoing
/// the copy operation.
operation SquareI (xs: LittleEndian, result: LittleEndian) : Unit {
body (...) {
(Controlled SquareI) (new Qubit[0], (xs, result));
}
controlled (controls, ...) {
let n = Length(xs!);
EqualityFactI(2 * n, Length(result!), "Integer multiplication
requires a 2n-bit result registers.");
AssertAllZero(result!);
using (anc = Qubit()) {
for (i in 0..n-1) {
(Controlled CNOT) (controls, (xs![i], anc));
(Controlled AddI) ([anc], (xs,
LittleEndian(result![i..i+n])));
(Controlled CNOT) (controls, (xs![i], anc));
}
}
}
adjoint auto;
adjoint controlled auto;
}
/// # Summary
/// Multiply signed integer `xs` by signed integer `ys` and store
/// the result in `result`, which must be zero initially.
///
/// # Input
/// ## xs
/// n-bit multiplicand (SignedLittleEndian)
/// ## ys
/// n-bit multiplier (SignedLittleEndian)
/// ## result
/// 2n-bit result (SignedLittleEndian), must be in state $\ket{0}$
/// initially.
operation MultiplySI (xs: SignedLittleEndian,
ys: SignedLittleEndian,
result: SignedLittleEndian): Unit {
body (...) {
(Controlled MultiplySI) (new Qubit[0], (xs, ys, result));
}
controlled (controls, ...) {
let n = Length(xs!!);
using ((signx, signy) = (Qubit(), Qubit())) {
CNOT(Tail(xs!!), signx);
CNOT(Tail(ys!!), signy);
(Controlled Invert2sSI)([signx], xs);
(Controlled Invert2sSI)([signy], ys);
(Controlled MultiplyI) (controls, (xs!, ys!, result!));
CNOT(signx, signy);
// No controls required since `result` will still be zero
// if we did not perform the multiplication above.
(Controlled Invert2sSI)([signy], result);
CNOT(signx, signy);
(Controlled Adjoint Invert2sSI)([signx], xs);
(Controlled Adjoint Invert2sSI)([signy], ys);
CNOT(Tail(xs!!), signx);
CNOT(Tail(ys!!), signy);
}
}
adjoint auto;
adjoint controlled auto;
}
/// # Summary
/// Square signed integer `xs` and store
/// the result in `result`, which must be zero initially.
///
/// # Input
/// ## xs
/// n-bit integer to square (SignedLittleEndian)
/// ## result
/// 2n-bit result (SignedLittleEndian), must be in state $\ket{0}$
/// initially.
///
/// # Remarks
/// The implementation relies on IntegerSquare.
operation SquareSI (xs: SignedLittleEndian,
result: SignedLittleEndian): Unit {
body (...) {
(Controlled SquareSI) (new Qubit[0], (xs, result));
}
controlled (controls, ...) {
let n = Length(xs!!);
using ((signx, signy) = (Qubit(), Qubit())) {
CNOT(Tail(xs!!), signx);
(Controlled Invert2sSI)([signx], xs);
(Controlled SquareI) (controls, (xs!, result!));
(Controlled Adjoint Invert2sSI)([signx], xs);
CNOT(Tail(xs!!), signx);
}
}
adjoint auto;
adjoint controlled auto;
}
}

Просмотреть файл

@ -0,0 +1,8 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
namespace Microsoft.Quantum.Arithmetic {
/// # Summary
/// Type of a signed integer stored in little endian (see LittleEndian).
newtype SignedLittleEndian = LittleEndian;
}

Просмотреть файл

@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<AssemblyName>Microsoft.Quantum.Numerics</AssemblyName>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<PropertyGroup>
<NoWarn>0162</NoWarn>
<RunQDocGen>True</RunQDocGen>
<Authors>Microsoft</Authors>
<Description>Microsoft's Quantum numerics libraries.</Description>
<Copyright>© Microsoft Corporation. All rights reserved.</Copyright>
<PackageReleaseNotes>See: https://docs.microsoft.com/en-us/quantum/relnotes/</PackageReleaseNotes>
<PackageLicenseUrl>https://github.com/Microsoft/QuantumLibraries/raw/master/LICENSE.txt</PackageLicenseUrl>
<PackageProjectUrl>https://github.com/Microsoft/QuantumLibraries</PackageProjectUrl>
<PackageIconUrl>https://secure.gravatar.com/avatar/bd1f02955b2853ba0a3b1cdc2434e8ec.png</PackageIconUrl>
<PackageTags>Quantum Q# Qsharp</PackageTags>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Standard\src\Standard.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Quantum.Development.Kit" Version="0.6.1905.301" />
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,376 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
namespace Microsoft.Quantum.Numerics.ToffoliTests {
open Microsoft.Quantum.Arrays;
open Microsoft.Quantum.Convert;
open Microsoft.Quantum.Math;
open Microsoft.Quantum.Intrinsic;
open Microsoft.Quantum.Arithmetic;
open Microsoft.Quantum.Diagnostics;
operation PrepareFxPTest() : Unit {
for (a in [1.2, 3.9, 3.14159, -0.6, -4.5, -3.1931, 0.0]){
using (xs = Qubit[10]) {
let fp = FixedPoint(4, xs);
PrepareFxP(a, fp);
let measured = MeasureFxP(fp);
EqualityFactB(AbsD(measured - a) <= 1./IntAsDouble(2^7), true,
$"FixedPoint initialized to {a} but measured {measured}.");
ResetAll(xs);
}
}
}
operation CompareGreaterThanFxPTest() : Unit {
for (a in [1.2, 3.9, 3.14159, -0.6, -4.5, -3.1931, 0.0]){
for (b in [1.1, 3.95, 3.14259, -0.4, -4.6, -3.931, 0.1]) {
using ((xs, ys, res) = (Qubit[10], Qubit[10], Qubit())) {
let fp1 = FixedPoint(4, xs);
let fp2 = FixedPoint(4, ys);
PrepareFxP(a, fp1);
PrepareFxP(b, fp2);
CompareGreaterThanFxP(fp1, fp2, res);
let measured = M(res);
EqualityFactB(a > b, measured == One,
$"FixedPoint comparison: {a} > {b} != {measured}.");
ResetAll(xs + ys + [res]);
}
}
}
}
operation AddConstantFxPTest() : Unit {
for (a in [1.2, 3.9, 3.14159, -0.6, -4.5, -3.1931, 0.0]){
for (b in [1.2, 3.9, 3.14159, -0.6, -4.5, -3.1931, 0.0]){
using (xs = Qubit[11]) {
let fp = FixedPoint(5, xs);
PrepareFxP(a, fp);
AddConstantFxP(b, fp);
let measured = MeasureFxP(fp);
EqualityWithinToleranceFact(measured, (a+b), 1. / IntAsDouble(2^6));
}
}
}
}
operation AddFxPTest() : Unit {
for (a in [1.2, 3.9, 3.14159, -0.6, -4.5, -3.1931, 0.0]){
for (b in [1.2, 3.9, 3.14159, -0.6, -4.5, -3.1931, 0.0]){
using ((xs, ys) = (Qubit[11], Qubit[11])) {
let fp1 = FixedPoint(5, xs);
let fp2 = FixedPoint(5, ys);
PrepareFxP(a, fp1);
PrepareFxP(b, fp2);
AddFxP(fp1, fp2);
let measured = MeasureFxP(fp2);
EqualityWithinToleranceFact(measured, (a+b), 1. / IntAsDouble(2^6));
ResetAll(xs + ys);
}
}
}
}
operation MultiplyFxPTest() : Unit {
for (pos in 5..8) {
for (a in [1.2, 3.9, 3.14159, -0.6, -3.5, -3.1931, 0.0]){
for (b in [1.2, 3.9, 3.14159, -0.6, -3.5, -3.1931, 0.0]){
using ((xs, ys, zs) = (Qubit[13], Qubit[13], Qubit[13])) {
let fp1 = FixedPoint(pos, xs);
let fp2 = FixedPoint(pos, ys);
let fp3 = FixedPoint(pos, zs);
PrepareFxP(a, fp1);
PrepareFxP(b, fp2);
MultiplyFxP(fp1, fp2, fp3);
let measured = MeasureFxP(fp3);
let eps = 1./IntAsDouble(2^(13-pos));
let epsTotal = AbsD(a) * eps + AbsD(b) * eps + eps * eps;
EqualityWithinToleranceFact(measured, a * b, epsTotal);
ResetAll(xs + ys + zs);
}
}
}
}
}
operation SquareFxPTest() : Unit {
for (pos in 5..8) {
for (a in [1.2, 3.9, 3.14159, -0.6, -3.5, -3.1931, 0.0]){
using ((xs, ys) = (Qubit[13], Qubit[13])) {
let fp1 = FixedPoint(pos, xs);
let fp2 = FixedPoint(pos, ys);
PrepareFxP(a, fp1);
SquareFxP(fp1, fp2);
let measured = MeasureFxP(fp2);
let eps = 1./IntAsDouble(2^(13-pos));
let epsTotal = 2. * AbsD(a) * eps + eps * eps;
EqualityWithinToleranceFact(measured, a * a, epsTotal);
ResetAll(xs + ys);
}
}
}
}
function _computeReciprocal(a : Double, n : Int, pos : Int, pos2 : Int) : Double {
let p = pos;
let intA = a >= 0. ? Floor(AbsD(a) * IntAsDouble(2^(n-p)) + 0.5)
| Ceiling(AbsD(a) * IntAsDouble(2^(n-p)) - 0.5);
let intDiv = 2^(2*n-1) / intA;
let aReciprUnsigned = IntAsDouble((intDiv >>> (p+pos2-1)) &&& (2^n-1)) / IntAsDouble(2^(n-pos2));
return (a >= 0. ? 1. | -1.) * aReciprUnsigned;
}
operation ComputeReciprocalFxPTest() : Unit {
for (pos in 5..8) {
for (pos2 in pos-1..pos+3) {
for (a in [1.2, 3.9, -0.314159, -0.6, -3.5, -3.1931, 0.127]){
let n = 20;
using ((xs, ys) = (Qubit[n], Qubit[n])) {
let fp1 = FixedPoint(pos, xs);
let fp2 = FixedPoint(pos, ys);
PrepareFxP(a, fp1);
ComputeReciprocalFxP(fp1, fp2);
let measured = MeasureFxP(fp2);
let eps = 1./IntAsDouble(2^(n-pos));
let eps2 = 1./IntAsDouble(2^(n-pos2));
let aEpsLarger = a + (a>=0. ? eps | -eps);
let aEpsSmaller = a - (a>=0. ? eps | -eps);
let res1 = _computeReciprocal(a+eps,n,pos,pos2);
let res2 = _computeReciprocal(a-eps,n,pos,pos2);
let minRes = MinD(res1, res2) - eps2;
let maxRes = MaxD(res1, res2) + eps2;
let isWithinTol = minRes <= measured and
maxRes >= measured;
EqualityFactB(isWithinTol,
true,
$"FixedPoint reciprocal 1/{a}: {measured} is not within [{minRes},{maxRes}].");
ResetAll(xs + ys);
}
}
}
}
}
operation SquareFxPCtrlTest() : Unit {
for (ctrl in 0..3) {
for (pos in 5..8) {
for (a in [1.2, 3.9, 3.14159, -0.6, -3.5, -3.1931, 0.0]){
using ((xs, ys, cs) = (Qubit[13], Qubit[13], Qubit[2])) {
ApplyXorInPlace(ctrl, LittleEndian(cs));
let fp1 = FixedPoint(pos, xs);
let fp2 = FixedPoint(pos, ys);
PrepareFxP(a, fp1);
(Controlled SquareFxP)(cs, (fp1, fp2));
let measured = MeasureFxP(fp2);
let eps = 1./IntAsDouble(2^(13-pos));
let epsTotal = 2. * AbsD(a) * eps + eps * eps;
if (ctrl == 3) {
EqualityWithinToleranceFact(measured, a * a, epsTotal);
}
else {
let measuredI = MeasureInteger(LittleEndian(ys));
EqualityFactI(measuredI, 0,
"Controlled FixedPoint square changed the result register!");
}
ResetAll(xs + ys + cs);
}
}
}
}
}
operation MultiplyFxPCtrlTest() : Unit {
for (ctrl in 0..3) {
for (pos in 5..8) {
for (a in [1.2, 3.9, 3.14159, -0.6, -3.5, -3.1931, 0.0]){
for (b in [1.2, 3.9, 3.14159, -0.6, -3.5, -3.1931, 0.0]){
using ((xs, ys, zs, cs) = (Qubit[13], Qubit[13], Qubit[13], Qubit[2])) {
ApplyXorInPlace(ctrl, LittleEndian(cs));
let fp1 = FixedPoint(pos, xs);
let fp2 = FixedPoint(pos, ys);
let fp3 = FixedPoint(pos, zs);
PrepareFxP(a, fp1);
PrepareFxP(b, fp2);
(Controlled MultiplyFxP)(cs, (fp1, fp2, fp3));
let measured = MeasureFxP(fp3);
let eps = 1./IntAsDouble(2^(13-pos));
let epsTotal = AbsD(a) * eps + AbsD(b) * eps + eps * eps;
if (ctrl == 3) {
EqualityWithinToleranceFact(measured, a * b, epsTotal);
}
else {
let measuredI = MeasureInteger(LittleEndian(zs));
EqualityFactI(measuredI, 0,
"Controlled FixedPoint multiplication changed the output register!");
}
ResetAll(xs + ys + zs + cs);
}
}
}
}
}
}
operation EvaluatePolynomialFxPTest() : Unit {
for (pos in 4..5) {
for (coeffs in [[1.3, -2.4, 1.9],
[-0.3, -0.2],
[0.1, 1.1, -0.1, 0.2],
[0.1, -0.1, 0.2, 0.2, -0.1, 0.3],
[0.2]]){
for (a in [0.0, 0.1, -0.1, 0.2, -0.2, 1.3, -1.3]){
let n = 20;
using ((xs, ys) = (Qubit[n], Qubit[n])) {
let fp1 = FixedPoint(pos, xs);
let fp2 = FixedPoint(pos, ys);
PrepareFxP(a, fp1);
EvaluatePolynomialFxP(coeffs, fp1, fp2);
let measured = MeasureFxP(fp2);
let eps = 1./IntAsDouble(2^(n-pos));
mutable epsTotal = 0.;
mutable errX = eps;
mutable result = Tail(coeffs);
set epsTotal = epsTotal + eps;
for (coeff in coeffs[Length(coeffs)-2..(-1)..0]) {
set epsTotal = epsTotal + AbsD(result) * eps
+ AbsD(a) * epsTotal + eps * epsTotal;
set result = result * a + coeff;
set epsTotal = epsTotal + eps;
}
EqualityWithinToleranceFact(measured, result, epsTotal);
ResetAll(xs + ys);
}
}
}
}
}
operation EvaluatePolynomialFxPCtrlTest() : Unit {
for (ctrl in 0..3) {
for (pos in 4..5) {
for (coeffs in [[1.3, -2.4, 1.9],
[-0.3, -0.2],
[0.2]]){
for (a in [0.0, 0.1, -0.2, 1.3, -1.3]){
let n = 20;
using ((xs, ys, ctrls) = (Qubit[n], Qubit[n], Qubit[2])) {
let fp1 = FixedPoint(pos, xs);
let fp2 = FixedPoint(pos, ys);
ApplyXorInPlace(ctrl, LittleEndian(ctrls));
PrepareFxP(a, fp1);
(Controlled EvaluatePolynomialFxP)(ctrls,
(coeffs, fp1, fp2));
let measured = MeasureFxP(fp2);
let eps = 1./IntAsDouble(2^(n-pos));
mutable epsTotal = 0.;
mutable errX = eps;
mutable result = Tail(coeffs);
set epsTotal = epsTotal + eps;
for (coeff in coeffs[Length(coeffs)-2..(-1)..0]) {
set epsTotal = epsTotal + AbsD(result) * eps
+ AbsD(a) * epsTotal + eps * epsTotal;
set result = result * a + coeff;
set epsTotal = epsTotal + eps;
}
if (ctrl == 3) {
EqualityWithinToleranceFact(measured, result, epsTotal);
}
else{
let measuredI = MeasureInteger(LittleEndian(ys));
EqualityFactI(measuredI,
0,
$"Controlled FixedPoint polynomial evaluation changed the output register!");
}
ResetAll(xs + ys + ctrls);
}
}
}
}
}
}
operation EvaluateOddPolynomialFxPTest() : Unit {
for (pos in 4..5) {
for (coeffs in [[1.3, -2.4, 1.9],
[-0.3],
[0.1, -0.1, 0.2, 0.2]]){
for (a in [0.0, 0.1, -0.1, 0.2, -0.2, 1.3, -1.3]){
let n = 20;
using ((xs, ys) = (Qubit[n], Qubit[n])) {
let fp1 = FixedPoint(pos, xs);
let fp2 = FixedPoint(pos, ys);
PrepareFxP(a, fp1);
EvaluateOddPolynomialFxP(coeffs, fp1, fp2);
let measured = MeasureFxP(fp2);
let eps = 1./IntAsDouble(2^(n-pos));
mutable epsTotal = 0.;
mutable errX = eps;
mutable result = Tail(coeffs);
set epsTotal = epsTotal + eps;
let aSquare = a * a;
for (coeff in coeffs[Length(coeffs)-2..(-1)..0]) {
set epsTotal = epsTotal + AbsD(result) * eps
+ AbsD(aSquare) * epsTotal + eps * epsTotal;
set result = result * aSquare + coeff;
set epsTotal = epsTotal + eps;
}
set epsTotal = epsTotal + AbsD(result) * eps + AbsD(a) * epsTotal
+ eps * epsTotal;
set result = result * a;
EqualityWithinToleranceFact(measured, result, epsTotal);
ResetAll(xs + ys);
}
}
}
}
}
operation EvaluateOddPolynomialFxPCtrlTest() : Unit {
for (ctrl in 0..3) {
for (pos in 4..5) {
for (coeffs in [[1.3, -2.4, 1.9],
[-0.3, -0.2],
[0.1, 1.1, -0.1, 0.2],
[0.1, -0.1, 0.2, 0.2, -0.1, 0.3],
[0.2]]){
for (a in [0.0, 0.1, -0.1, 0.2, -0.2, 1.3, -1.3]){
let n = 20;
using ((xs, ys, ctrls) = (Qubit[n], Qubit[n], Qubit[2])) {
let fp1 = FixedPoint(pos, xs);
let fp2 = FixedPoint(pos, ys);
PrepareFxP(a, fp1);
EvaluateOddPolynomialFxP(coeffs, fp1, fp2);
let measured = MeasureFxP(fp2);
let eps = 1./IntAsDouble(2^(n-pos));
mutable epsTotal = 0.;
mutable errX = eps;
mutable result = Tail(coeffs);
set epsTotal = epsTotal + eps;
let aSquare = a * a;
for (coeff in coeffs[Length(coeffs)-2..(-1)..0]) {
set epsTotal = epsTotal + AbsD(result) * eps
+ AbsD(aSquare) * epsTotal + eps * epsTotal;
set result = result * aSquare + coeff;
set epsTotal = epsTotal + eps;
}
set epsTotal = epsTotal + AbsD(result) * eps + AbsD(a) * epsTotal
+ eps * epsTotal;
set result = result * a;
if (ctrl == 3) {
EqualityWithinToleranceFact(measured, result, epsTotal);
}
else{
let measuredI = MeasureInteger(LittleEndian(ys));
EqualityFactI(measuredI,
0,
$"Controlled FixedPoint polynomial evaluation changed the output register!");
}
ResetAll(xs + ys + ctrls);
}
}
}
}
}
}
}

Просмотреть файл

@ -0,0 +1,321 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
namespace Microsoft.Quantum.Numerics.ToffoliTests {
open Microsoft.Quantum.Diagnostics;
open Microsoft.Quantum.Intrinsic;
open Microsoft.Quantum.Arithmetic;
open Microsoft.Quantum.Extensions.Math;
operation MultiplyIExhaustiveTest () : Unit {
ExhaustiveTestHelper2Args(IntegerMultiplicationRun(false, _, _, _, _));
}
operation SquareIExhaustiveTest () : Unit {
ExhaustiveTestHelper1Arg(IntegerSquareRun(false, _, _, _));
}
operation DivideIExhaustiveTest () : Unit {
ExhaustiveTestHelper2Args(IntegerDivisionRun);
}
operation SquareSIExhaustiveTest () : Unit {
ExhaustiveTestHelper1Arg(IntegerSquareRun(true, _, _, _));
}
operation CompareGTSIExhaustiveTest () : Unit {
ExhaustiveTestHelper2Args(IntegerGreaterThanRun(true, _, _, _, _));
}
operation MultiplySIExhaustiveTest () : Unit {
ExhaustiveTestHelper2Args(IntegerMultiplicationRun(true, _, _, _, _));
}
operation ComputeReciprocalIExhaustiveTest () : Unit {
ExhaustiveTestHelper1Arg(IntegerReciprocalRun(false, _, _, _));
}
operation IntegerGreaterThanRun(signed: Bool, a: Int, b: Int,
n: Int, numCtrl: Int) : Unit {
using ((aqs, bqs, result, ctrlqs) = (Qubit[n], Qubit[n],
Qubit(), Qubit[numCtrl])) {
ApplyXorInPlace(a, LittleEndian(aqs));
ApplyXorInPlace(b, LittleEndian(bqs));
if (signed) {
CompareGTSI(
SignedLittleEndian(LittleEndian(aqs)),
SignedLittleEndian(LittleEndian(bqs)),
result);
}
else {
CompareGTI(LittleEndian(aqs),
LittleEndian(bqs),
result);
}
mutable asigned = a;
mutable bsigned = b;
if (signed and a >= 2^(n-1)) {
set asigned = -2^n+a;
}
if (signed and b >= 2^(n-1)) {
set bsigned = -2^n+b;
}
mutable res = asigned > bsigned;
mutable resMeasured = M(result);
EqualityFactB(res, resMeasured == One,
$"Integer comparison failed:
{asigned} > {bsigned} = {res} != {resMeasured} [n={n}]");
ResetAll(aqs + bqs + [result]);
for (ctrlState in 0..2^numCtrl-1) {
ApplyXorInPlace(ctrlState, LittleEndian(ctrlqs));
ApplyXorInPlace(a, LittleEndian(aqs));
ApplyXorInPlace(b, LittleEndian(bqs));
if (signed) {
(Controlled CompareGTSI) (ctrlqs,
(SignedLittleEndian(LittleEndian(aqs)),
SignedLittleEndian(LittleEndian(bqs)),
result));
}
else {
(Controlled CompareGTI) (ctrlqs,
(LittleEndian(aqs),
LittleEndian(bqs),
result));
}
set res = asigned > bsigned;
if (ctrlState < 2^numCtrl-1) {
set res = false;
}
set resMeasured = M(result);
EqualityFactB(res, resMeasured == One,
$"Controlled integer comparison failed.");
ResetAll(aqs + bqs + [result] + ctrlqs);
}
}
}
operation IntegerMultiplicationRun(signed: Bool, a: Int, b: Int,
n: Int, numCtrl: Int) : Unit {
using ((aqs, bqs, cqs, ctrlqs) = (Qubit[n], Qubit[n],
Qubit[2*n], Qubit[numCtrl])) {
ApplyXorInPlace(a, LittleEndian(aqs));
ApplyXorInPlace(b, LittleEndian(bqs));
if (signed) {
MultiplySI(
SignedLittleEndian(LittleEndian(aqs)),
SignedLittleEndian(LittleEndian(bqs)),
SignedLittleEndian(LittleEndian(cqs)));
}
else {
MultiplyI (LittleEndian(aqs),
LittleEndian(bqs),
LittleEndian(cqs));
}
mutable asigned = a;
mutable bsigned = b;
if (signed and a >= 2^(n-1)) {
set asigned = -2^n+a;
}
if (signed and b >= 2^(n-1)) {
set bsigned = -2^n+b;
}
mutable c = asigned * bsigned;
mutable cMeasured = MeasureInteger(LittleEndian(cqs));
if (signed and cMeasured >= 2^(2*n-1)){
set cMeasured = -2^(2*n) + cMeasured;
}
EqualityFactI(c, cMeasured,
$"Multiplication did not yield the correct result:
{asigned} * {bsigned} = {c} != {cMeasured} [n={n}]");
ResetAll(aqs + bqs + cqs);
for (ctrlState in 0..2^numCtrl-1) {
ApplyXorInPlace(ctrlState, LittleEndian(ctrlqs));
ApplyXorInPlace(a, LittleEndian(aqs));
ApplyXorInPlace(b, LittleEndian(bqs));
if (signed) {
(Controlled MultiplySI) (ctrlqs,
(SignedLittleEndian(LittleEndian(aqs)),
SignedLittleEndian(LittleEndian(bqs)),
SignedLittleEndian(LittleEndian(cqs))));
}
else {
(Controlled MultiplyI) (ctrlqs,
(LittleEndian(aqs),
LittleEndian(bqs),
LittleEndian(cqs)));
}
set c = asigned * bsigned;
if (ctrlState != 2^numCtrl-1) {
set c = 0;
}
set cMeasured = MeasureInteger(LittleEndian(cqs));
if (signed and cMeasured >= 2^(2*n-1)){
set cMeasured = -2^(2*n) + cMeasured;
}
EqualityFactI(c, cMeasured,
"Controlled multiplication did not yield the correct result.");
ResetAll(aqs + bqs + cqs + ctrlqs);
}
}
}
operation IntegerSquareRun(signed: Bool, a: Int,
n: Int, numCtrl: Int) : Unit {
using ((aqs, cqs, ctrlqs) = (Qubit[n], Qubit[2*n],
Qubit[numCtrl])) {
ApplyXorInPlace(a, LittleEndian(aqs));
if (signed) {
SquareSI(SignedLittleEndian(LittleEndian(aqs)),
SignedLittleEndian(LittleEndian(cqs)));
}
else {
SquareI(LittleEndian(aqs), LittleEndian(cqs));
}
mutable signeda = a;
if (signed and a >= 2^(n-1)) {
set signeda = -2^n + a;
}
mutable c = signeda * signeda;
mutable cMeasured = MeasureInteger(LittleEndian(cqs));
if (signed and cMeasured >= 2^(2*n-1)){
set cMeasured = -2^(2*n) + cMeasured;
}
EqualityFactI(c, cMeasured,
"Square did not yield the correct result.");
ResetAll(aqs + cqs);
for (ctrlState in 0..2^numCtrl-1) {
ApplyXorInPlace(ctrlState, LittleEndian(ctrlqs));
ApplyXorInPlace(a, LittleEndian(aqs));
if (signed) {
(Controlled SquareSI) (ctrlqs,
(SignedLittleEndian(LittleEndian(aqs)),
SignedLittleEndian(LittleEndian(cqs))));
}
else {
(Controlled SquareI) (ctrlqs,
(LittleEndian(aqs), LittleEndian(cqs)));
}
set c = signeda * signeda;
if (ctrlState != 2^numCtrl-1) {
set c = 0;
}
set cMeasured = MeasureInteger(LittleEndian(cqs));
if (signed and cMeasured >= 2^(2*n-1)){
set cMeasured = -2^(2*n) + cMeasured;
}
EqualityFactI(c, cMeasured,
"Controlled square did not yield the correct result.");
ResetAll(aqs + cqs + ctrlqs);
}
}
}
operation IntegerReciprocalRun(signed: Bool, a: Int,
n: Int, numCtrl: Int) : Unit {
using ((aqs, cqs, ctrlqs) = (Qubit[n], Qubit[2*n],
Qubit[numCtrl])) {
ApplyXorInPlace(a, LittleEndian(aqs));
ComputeReciprocalI(LittleEndian(aqs), LittleEndian(cqs));
mutable c = a > 0 ? 2^(2*n-1) / a | (2^(2*n)-1);
mutable cMeasured = MeasureInteger(LittleEndian(cqs));
mutable aMeasured = MeasureInteger(LittleEndian(aqs));
EqualityFactI(a, aMeasured,
"Reciprocal modified the input.");
EqualityFactI(c, cMeasured,
$"Reciprocal did not yield the correct result:
1/{a} = {c} != {cMeasured}.");
ResetAll(aqs + cqs);
for (ctrlState in 0..2^numCtrl-1) {
ApplyXorInPlace(ctrlState, LittleEndian(ctrlqs));
ApplyXorInPlace(a, LittleEndian(aqs));
(Controlled ComputeReciprocalI) (ctrlqs,
(LittleEndian(aqs), LittleEndian(cqs)));
set c = a > 0 ? 2^(2*n-1) / a | (2^(2*n)-1);
if (ctrlState != 2^numCtrl-1) {
set c = 0;
}
set cMeasured = MeasureInteger(LittleEndian(cqs));
EqualityFactI(c, cMeasured,
$"Controlled reciprocal did not yield the correct result:
1/{a} = {c} != {cMeasured}.");
set aMeasured = MeasureInteger(LittleEndian(aqs));
EqualityFactI(a, aMeasured,
"Reciprocal modified the input.");
ResetAll(aqs + cqs + ctrlqs);
}
}
}
operation IntegerDivisionRun(a: Int, b: Int, n: Int, numCtrl: Int): Unit {
using ((aqs, bqs, cqs, ctrlqs) = (Qubit[n], Qubit[n],
Qubit[n], Qubit[numCtrl])) {
mutable c = 0;
if (b > 0) {
set c = a / b;
}
else{
set c = 2^n - 1;
}
let rem = a - c * b;
ApplyXorInPlace(a, LittleEndian(aqs));
ApplyXorInPlace(b, LittleEndian(bqs));
DivideI (LittleEndian(aqs), LittleEndian(bqs),
LittleEndian(cqs));
mutable resMeasured = MeasureInteger(LittleEndian(cqs));
mutable remMeasured = MeasureInteger(LittleEndian(aqs));
EqualityFactI(c, resMeasured,
$"Controlled division did not yield the correct result:
{a} / {b} = {c} != {resMeasured}");
EqualityFactI(remMeasured, rem,
"Controlled division did not yield the correct remainder:
rem = {rem} != {remMeasured} for {a} / {b}");
ResetAll(aqs + bqs + cqs + ctrlqs);
for (ctrlState in 0..2^numCtrl-1) {
ApplyXorInPlace(ctrlState, LittleEndian(ctrlqs));
ApplyXorInPlace(a, LittleEndian(aqs));
ApplyXorInPlace(b, LittleEndian(bqs));
(Controlled DivideI) (ctrlqs,
(LittleEndian(aqs), LittleEndian(bqs), LittleEndian(cqs)));
set resMeasured = MeasureInteger(LittleEndian(cqs));
set remMeasured = MeasureInteger(LittleEndian(aqs));
if (ctrlState == 2^numCtrl-1) {
EqualityFactI(c, resMeasured,
$"Controlled division did not yield the correct result:
{a} / {b} = {c} != {resMeasured}");
EqualityFactI(remMeasured, rem,
"Controlled division did not yield the correct remainder:
rem = {rem} != {remMeasured} for {a} / {b}");
}
else {
EqualityFactI(0, resMeasured,
"Controlled division was not trivial.");
EqualityFactI(a, remMeasured,
"Controlled division was not trivial.");
}
ResetAll(aqs + bqs + cqs + ctrlqs);
}
}
}
operation ExhaustiveTestHelper1Arg (TestFunction: ((Int, Int, Int) => Unit)) : Unit {
for (numCtrlQubits in 0..2) {
for (numQubits in 1..5) {
for (a in 0..2^numQubits-1) {
TestFunction(a, numQubits, numCtrlQubits);
}
}
}
}
operation ExhaustiveTestHelper2Args (TestFunction: ((Int, Int, Int, Int) => Unit)) : Unit {
for (numCtrlQubits in 0..2) {
for (numQubits in 1..5) {
for (a in 0..2^numQubits-1) {
for (b in 0..2^numQubits-1) {
TestFunction(a, b, numQubits, numCtrlQubits);
}
}
}
}
}
}

Просмотреть файл

@ -0,0 +1,52 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using Microsoft.Quantum.Simulation.XUnit;
using Microsoft.Quantum.Simulation.Simulators;
using Xunit.Abstractions;
using System.Diagnostics;
namespace Microsoft.Quantum.Numerics.Tests
{
public class NumericsTests
{
private readonly ITestOutputHelper output;
public NumericsTests(ITestOutputHelper output)
{
this.output = output;
}
/// <summary>
/// This driver will run all Q# tests (operations named "...Test")
/// that are located inside Microsoft.Quantum.Numerics.Tests using the quantum
/// simulator.
/// </summary>
[OperationDriver(TestNamespace = "Microsoft.Quantum.Numerics.Tests",
TestCasePrefix = "QSim:")]
public void QSimTests(TestOperation op)
{
var sim = new QuantumSimulator();
// OnLog defines action(s) performed when Q# test calls function Message
sim.OnLog += (msg) => { output.WriteLine(msg); };
sim.OnLog += (msg) => { Debug.WriteLine(msg); };
op.TestOperationRunner(sim);
}
/// <summary>
/// This driver will run all Q# tests (operations named "...Test")
/// that are located inside Microsoft.Quantum.Numerics.ToffoliTests using the
/// Toffoli simulator.
/// </summary>
[OperationDriver(TestNamespace = "Microsoft.Quantum.Numerics.ToffoliTests",
TestCasePrefix = "ToffSim:")]
public void ToffoliSimTests(TestOperation op)
{
var sim = new ToffoliSimulator();
// OnLog defines action(s) performed when Q# test calls function Message
sim.OnLog += (msg) => { output.WriteLine(msg); };
sim.OnLog += (msg) => { Debug.WriteLine(msg); };
op.TestOperationRunner(sim);
}
}
}

Просмотреть файл

@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
<PlatformTarget>x64</PlatformTarget>
<IsPackable>false</IsPackable>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<PropertyGroup>
<NoWarn>0162</NoWarn>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\src\Numerics.csproj" />
<ProjectReference Include="..\..\Standard\src\Standard.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Quantum.Development.Kit" Version="0.6.1905.301" />
<PackageReference Include="Microsoft.Quantum.Xunit" Version="0.6.1905.301" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" />
<PackageReference Include="xunit" Version="2.4.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
<DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
</ItemGroup>
</Project>