Skip to content
This repository was archived by the owner on Jan 12, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 134 additions & 0 deletions Standard/src/Arrays/Sorted.qs
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

namespace Microsoft.Quantum.Arrays {
open Microsoft.Quantum.Intrinsic;
open Microsoft.Quantum.Canon;

/// # Summary
/// Given two sorted arrays, returns a single array containing the
/// elements of both in sorted order. Used internally by merge sort.
internal function Merged<'T>(comparison : (('T, 'T) -> Bool), left : 'T[], right : 'T[]) : 'T[] {
mutable result = new 'T[0];
mutable l = left;
mutable r = right;
while ((not IsEmpty(l)) and (not IsEmpty(r))) {
if (comparison(Head(l), Head(r))) {
set result += [Head(l)];
set l = Rest(l);
} else {
set result += [Head(r)];
set r = Rest(r);
}
}

// Note that at this point, either or both of l and r are empty,
// such that we can simply append both to our result to get the
// whole merged array.
return result + l + r;
}

/// # Summary
/// Given an array, returns whether that array is sorted as defined by
/// a given comparison function.
///
/// # Type Parameters
/// ## 'T
/// The type of each element of `array`.
///
/// # Input
/// ## comparison
/// A function that compares two elements such that `a` is considered to
/// be less than or equal to `b` if `comparison(a, b)` is `true`.
/// ## array
/// The array to be checked.
///
/// # Output
/// `true` if and only if for each pair of elements `a` and `b` of
/// `array` occuring in that order, `comparison(a, b)` is `true`.
///
/// # Remarks
/// The function `comparison` is assumed to be transitive, such that
/// if `comparison(a, b)` and `comparison(b, c)`, then `comparison(a, c)`
/// is assumed. If this property does not hold, then the output of this
/// function may be incorrect.
function IsSorted<'T>(comparison : (('T, 'T) -> Bool), array : 'T[]) : Bool {
return All(
comparison,
Zip(Most(array), Rest(array))
);
}

/// # Summary
/// Given an array, returns the elements of that array sorted by a given
/// comparison function.
///
/// # Type Parameters
/// ## 'T
/// The type of each element of `array`.
///
/// # Input
/// ## comparison
/// A function that compares two elements such that `a` is considered to
/// be less than or equal to `b` if `comparison(a, b)` is `true`.
/// ## array
/// The array to be sorted.
///
/// # Ouput
/// An array containing the same elements as `array`, such that for all
/// elements `a` occuring earlier than elements `b`, `comparison(a, b)`
/// is `true`.
///
/// # Example
/// The following snippet sorts an array of integers to occur in ascending
/// order:
/// ```Q#
/// let sortedArray = Sorted(LessThanOrEqualI, [3, 17, 11, -201, -11]);
/// ```
///
/// # Remarks
/// The function `comparison` is assumed to be transitive, such that
/// if `comparison(a, b)` and `comparison(b, c)`, then `comparison(a, c)`
/// is assumed. If this property does not hold, then the output of this
/// function may be incorrect.
///
/// As this is a function, the results are completely determinstic, even
/// when two elements are considered equal under `comparison`;
/// that is, when `comparison(a, b)` and `comparison(b, a)` are both `true`.
/// In particular, the sort performed by this function is guaranteed to be
/// stable, so that if two elements `a` and `b` occur in that order within
/// `array` and are considered equal under `comparison`, then `a` will also
/// appear before `b` in the output.
///
/// For example:
/// ```Q#
/// function LastDigitLessThanOrEqual(left : Int, right : Int) : Bool {
/// return LessThanOrEqualI(
/// left % 10, right % 10
/// );
/// }
///
/// function SortedByLastDigit() : Int[] {
/// return Sorted(LastDigitLessThanOrEqual, [3, 37, 11, 17]);
/// }
/// // returns [11, 3, 37, 17].
/// ```
function Sorted<'T>(comparison : (('T, 'T) -> Bool), array : 'T[]) : 'T[] {
if (Length(array) <= 1) {
return array;
} else {
let idxPivot = Length(array) / 2;
let left = array[...idxPivot - 1];
let right = array[idxPivot...];

// Sort each sublist, then merge them back into a single combined
// list and return.
return Merged<'T>(
comparison,
Sorted(comparison, left),
Sorted(comparison, right)
);
}
}

}
2 changes: 1 addition & 1 deletion Standard/src/Arrays/Zip.qs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

namespace Microsoft.Quantum.Arrays {
Expand Down
85 changes: 85 additions & 0 deletions Standard/src/Logical/Comparisons.qs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

namespace Microsoft.Quantum.Logical {
open Microsoft.Quantum.Arrays;
open Microsoft.Quantum.Intrinsic;
open Microsoft.Quantum.Canon;

/// # Summary
/// Given a comparison function, returns a new function that
/// lexographically compares two arrays.
///
/// # Type Parameters
/// ## 'T
/// The type of the elements of the arrays being compared.
///
/// # Input
/// ## elementComparison
/// A function that compares two elements `x` and `y` and returns if
/// `x` is less than or equal to `y`.
///
/// # Output
/// A function that compares two arrays `xs` and `ys` and returns if
/// `xs` occurs before or equal to `ys` in lexographical ordering.
///
/// # Remarks
/// The lexographic comparison between two arrays `xs` and `ys` is defined
/// by the following procedure. We say that two elements `x` and `y`
/// are equivalent if `elementComparison(x, y)` and `elementComparison(y, x)`
/// are both true.
///
/// - Both arrays are compared element-by-element until the first pair of
/// elements that are not equivalent. The array containing the element
/// that occurs first according to `elementComparison` is said to occur
/// first in lexographical ordering.
/// - If no inequivalent elements are found, and one array is longer than
/// the other, the shorter array is said to occur first.
///
/// # Examples
/// ```Q#
/// let arrayComparison = LexographicComparison(LessThanOrEqualD);
/// let data = [
/// [1.1, 2.2, 3.3],
/// [1.1, 2.2],
/// [0.2, 2.2],
/// [1.1, 2.7]
/// ];
/// let sorted = Sorted(arrayComparison, data);
/// // sorted:
/// // [
/// // [0.2, 2.2],
/// // [1.1, 2.2],
/// // [1.1, 2.2, 3.3],
/// // [1.1, 2.7]
/// // ];
/// ```
///
/// # See Also
/// - Microsoft.Quantum.Arrays.Sorted
function LexographicComparison<'T>(elementComparison : (('T, 'T) -> Bool)) : (('T[], 'T[]) -> Bool) {
return LessThanLexographic(elementComparison, _, _);
}

/// # Summary
/// Used to implement `LexographicComparison`.
internal function LessThanLexographic<'T>(comparison : (('T, 'T) -> Bool), left : 'T[], right : 'T[]) : Bool {
for ((l, r) in Zip(left, right)) {
let lessThanOrEqual = comparison(l, r);
let greaterThanOrEqual = comparison(r, l);
let equal = lessThanOrEqual and greaterThanOrEqual;
if (lessThanOrEqual and not equal) {
return true;
} elif (greaterThanOrEqual and not equal) {
return false;
}
}

// At this point, all items in the common prefix of both arrays
// are equal to each other under comparison (l ≤ r and r ≤ l).
// Thus, if left is shorter than or equal to right, then left occurs
// at or before right in lexographical ordering.
return Length(left) <= Length(right);
}

}
76 changes: 76 additions & 0 deletions Standard/tests/Arrays/SortedTests.qs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

namespace Microsoft.Quantum.Arrays {
open Microsoft.Quantum.Random;
open Microsoft.Quantum.Diagnostics;
open Microsoft.Quantum.Logical;

@Test("QuantumSimulator")
function IntsAreSorted() : Unit {
Fact(IsSorted(LessThanOrEqualI, [1, 10, 100]), "[1, 10, 100] was marked as unsorted.");
}

@Test("QuantumSimulator")
function DoublesAreSorted() : Unit {
Fact(IsSorted(LessThanOrEqualD, [1.0, 10.1, 100.2]), "[1.0, 10.1, 100.2] was marked as unsorted.");
}

@Test("QuantumSimulator")
function IntsAreNotSorted() : Unit {
Contradiction(IsSorted(LessThanOrEqualI, [100, 10, 3]), "[100, 10, 3] was marked as sorted.");
}

@Test("QuantumSimulator")
function SortedIntsAreSorted() : Unit {
Fact(IsSorted(LessThanOrEqualI,
Sorted(LessThanOrEqualI, [100, 10, 3])),
"Sorted(<=, [100, 10, 3]) was marked as unsorted."
);
}

@Test("QuantumSimulator")
function SortedDoublesAreSorted() : Unit {
Fact(IsSorted(LessThanOrEqualD,
Sorted(LessThanOrEqualD, [100.0, 10.1, 3.14])),
"Sorted(<=, [100.0, 10.1, 3.14]) was marked as unsorted."
);
}

@Test("QuantumSimulator")
operation CheckRandomArraysAreSortedWhenSorted() : Unit {
let nItems = 100;
let nTrials = 10;
let maxItem = 1000;
for (_ in 0..nTrials - 1) {
let data = DrawMany((DiscreteUniformDistribution(0, maxItem))::Sample, nItems, ());
Fact(IsSorted(LessThanOrEqualI, Sorted(LessThanOrEqualI, data)), $"{data} was not sorted after running Sorted.");
}
}

@Test("QuantumSimulator")
function LexographicSortIsCorrect() : Unit {
let arrayComparison = LexographicComparison(LessThanOrEqualI);
let data = [
[1, 2, 3],
[1, 2],
[0, 2],
[1, 3]
];
let sorted = Sorted(arrayComparison, data);

AllEqualityFactI(
sorted[0], [0, 2], "0th item was not correct."
);
AllEqualityFactI(
sorted[1], [1, 2], "1st item was not correct."
);
AllEqualityFactI(
sorted[2], [1, 2, 3], "2nd item was not correct."
);
AllEqualityFactI(
sorted[3], [1, 3], "3rd item was not correct."
);
}

}
38 changes: 38 additions & 0 deletions Standard/tests/Logical/ComparisonTests.qs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

namespace Microsoft.Quantum.Tests {
open Microsoft.Quantum.Logical;
open Microsoft.Quantum.Diagnostics;
open Microsoft.Quantum.Math;
open Microsoft.Quantum.Arrays;

function LexographicComparisonIsCorrect() : Unit {
let lexographicComparison = LexographicComparison(LessThanOrEqualD);
Fact(
lexographicComparison(
[1.1, 2.2], [1.1, 2.2, 3.3]
),
"Shorter array should have occured first."
);
Fact(
lexographicComparison(
[0.7, 2.2], [1.1, 2.2]
),
"Array with smaller first element should have occured first."
);
Fact(
lexographicComparison(
[1.1, 2.2], [1.1, 2.2]
),
"Identical arrays should be marked as less than or equal."
);
Contradiction(
lexographicComparison(
[1.1, 2.7], [1.1, 2.2, 3.3]
),
"Array with larger second element should have occured second."
);
}

}