diff --git a/Standard/src/Arrays/Arrays.qs b/Standard/src/Arrays/Arrays.qs index 32e5ef991ab..0baf6abc184 100644 --- a/Standard/src/Arrays/Arrays.qs +++ b/Standard/src/Arrays/Arrays.qs @@ -63,10 +63,6 @@ namespace Microsoft.Quantum.Arrays { return array[0 .. Length(array) - 2]; } - internal function Lookup<'T> (array : 'T[], index : Int) : 'T { - return array[index]; - } - /// # Summary /// Given an array, returns a function which returns elements of that /// array. @@ -90,7 +86,7 @@ namespace Microsoft.Quantum.Arrays { /// where functions are used to avoid the need to record an entire array /// in memory. function LookupFunction<'T> (array : 'T[]) : (Int -> 'T) { - return Lookup(array, _); + return ElementAt(_, array); } /// # Summary @@ -129,6 +125,40 @@ namespace Microsoft.Quantum.Arrays { return array[0]; } + /// # Summary + /// Returns a tuple of first and all remaining elements of the array. + /// + /// # Type Parameters + /// ## 'A + /// The type of the array elements. + /// + /// # Input + /// ## array + /// An array with at least one element. + /// + /// # Output + /// A tuple of first and all remaining elements of the array. + function HeadAndRest<'A>(array : 'A[]) : ('A, 'A[]) { + return (Head(array), Rest(array)); + } + + /// # Summary + /// Returns a tuple of all but one and the last element of the array. + /// + /// # Type Parameters + /// ## 'A + /// The type of the array elements. + /// + /// # Input + /// ## array + /// An array with at least one element. + /// + /// # Output + /// A tuple of all but one and the last element of the array. + function MostAndTail<'A>(array : 'A[]) : ('A[], 'A) { + return (Most(array), Tail(array)); + } + /// # Summary /// Creates an array of given length with all elements equal to given value. /// diff --git a/Standard/src/Arrays/Filter.qs b/Standard/src/Arrays/Filter.qs index f03605ef773..38a7c463637 100644 --- a/Standard/src/Arrays/Filter.qs +++ b/Standard/src/Arrays/Filter.qs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace Microsoft.Quantum.Arrays { @@ -40,7 +40,7 @@ namespace Microsoft.Quantum.Arrays { /// } /// ``` /// The outcome one should expect from this example will be an array of numbers greater than 5. - function Filtered<'T> (predicate : ('T -> Bool), array : 'T[]) : 'T[] { + function Filtered<'T>(predicate : ('T -> Bool), array : 'T[]) : 'T[] { mutable totalFound = 0; mutable idxArray = new Int[Length(array)]; @@ -80,4 +80,45 @@ namespace Microsoft.Quantum.Arrays { ); } + /// # Summary + /// Given an array and a predicate that is defined + /// for the elements of the array, returns the number of elements + /// an array that consists of those elements that satisfy the predicate. + /// + /// # Remarks + /// The function is defined for generic types, i.e., whenever we have + /// an array `'T[]` and a predicate `'T -> Bool` we can filter elements. + /// + /// # Type Parameters + /// ## 'T + /// The type of `array` elements. + /// + /// # Input + /// ## predicate + /// A function from `'T` to Boolean that is used to filter elements. + /// ## array + /// An array of elements over `'T`. + /// + /// # Output + /// The number of elements in `array` that satisfy the predicate. + /// + /// # Example + /// The following code demonstrates the "Count" function. + /// A predicate is defined using the @"microsoft.quantum.logical.greaterthani" function: + /// ```Q# + /// let predicate = GreaterThanI(_, 5); + /// let count = Count(predicate, [2, 5, 9, 1, 8]); + /// // count = 2 + /// ``` + function Count<'T>(predicate : ('T -> Bool), array : 'T[]) : Int { + mutable totalFound = 0; + + for (element in array) { + if (predicate(element)) { + set totalFound += 1; + } + } + + return totalFound; + } } diff --git a/Standard/src/Arrays/Interleaved.qs b/Standard/src/Arrays/Interleaved.qs new file mode 100644 index 00000000000..05cbc93b6e4 --- /dev/null +++ b/Standard/src/Arrays/Interleaved.qs @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Microsoft.Quantum.Arrays { + open Microsoft.Quantum.Diagnostics; + + /// # Summary + /// Interleaves two arrays of (almost) same size. + /// + /// # Description + /// This function returns the interleaving of two arrays, starting + /// with the first element from the first array, then the first + /// element from the second array, and so on. + /// + /// The first array must either be + /// of the same length as the second one, or can have one more element. + /// + /// # Type Parameters + /// ## 'T + /// The type of each element of `first` and `second`. + /// + /// # Input + /// ## first + /// The first array to be interleaved. + /// + /// ## second + /// The second array to be interleaved. + /// + /// # Output + /// Interleaved array + /// + /// # Example + /// ```Q# + /// // same as int1 = [1, -1, 2, -2, 3, -3] + /// let int1 = Interleaved([1, 2, 3], [-1, -2, -3]) + /// + /// // same as int2 = [false, true, false, true, false] + /// let int2 = Interleaved(ConstantArray(3, false), ConstantArray(2, true)); + /// ``` + function Interleaved<'T>(first : 'T[], second : 'T[]) : 'T[] { + let lFirst = Length(first); + let lSecond = Length(second); + + Fact(lFirst >= lSecond and lFirst - lSecond <= 1, "Array `first` is either of same size as `second`, or has one more element"); + + return new 'T[lFirst + lSecond] + w/ 0..2..(lFirst + lSecond - 1) <- first + w/ 1..2..(lFirst + lSecond - 1) <- second; + } +} diff --git a/Standard/src/Arrays/Map.qs b/Standard/src/Arrays/Map.qs index e6e28337d0e..b541190a738 100644 --- a/Standard/src/Arrays/Map.qs +++ b/Standard/src/Arrays/Map.qs @@ -2,6 +2,7 @@ // Licensed under the MIT License. namespace Microsoft.Quantum.Arrays { + open Microsoft.Quantum.Math; /// # Summary /// Given an array and a function that is defined @@ -86,6 +87,109 @@ namespace Microsoft.Quantum.Arrays { return resultArray; } + /// # Summary + /// Given a range and a function that takes an integer as input, + /// returns a new array that consists + /// of the images of the range values under the function. + /// + /// # Remarks + /// The function is defined for generic types, i.e., whenever we have + /// a function `mapper: Int -> 'T` we can map the values + /// of the range and produce an array of type `'T[]`. + /// + /// # Type Parameters + /// ## 'T + /// The result type of the `mapper` function. + /// + /// # Input + /// ## mapper + /// A function from `Int` to `'T` that is used to map range values. + /// ## range + /// A range of integers. + /// + /// # Output + /// An array `'T[]` of elements that are mapped by the `mapper` function. + /// + /// # Example + /// This example adds 1 to a range of even numbers: + /// ```Q# + /// let numbers = MappedOverRange(PlusI(1, _), 0..2..10); + /// // numbers = [1, 3, 5, 7, 9, 11] + /// ``` + /// + /// # See Also + /// - Microsoft.Quantum.Arrays.Mapped + function MappedOverRange<'T> (mapper : (Int -> 'T), range : Range) : 'T[] { + let start = RangeStart(range); + let step = RangeStep(range); + let end = RangeEnd(range); + if ((end - start) / step >= 0) { + let nTerms = (end - start) / step + 1; + mutable resultArray = new 'T[nTerms]; + mutable idxElement = 0; + for (elem in range) { + set resultArray w/= idxElement <- mapper(elem); + set idxElement += 1; + } + return resultArray; + } else { + return new 'T[0]; + } + } + + /// # Summary + /// Given an array and a function that maps an array element to some output + /// array, returns the concatenated output arrays for each array element. + /// + /// # Type Parameters + /// ## 'TInput + /// The type of `array` elements. + /// ## 'TOutput + /// The `mapper` function returns arrays of this type. + /// + /// # Input + /// ## mapper + /// A function from `'TInput` to `'TOutput[]` that is used to map array elements. + /// ## array + /// An array of elements. + /// + /// # Output + /// An array of `'TOutput[]` which is the concatenation of all arrays generated by + /// the mapping function. + /// + /// # Example + /// ```Q# + /// let Numbers = SequenceI(1, _); // generates numbers starting from 1 + /// let values = FlatMapped(Numbers, [1, 2, 3]); + /// // values = [1, 1, 2, 1, 2, 3] + /// ``` + function FlatMapped<'TInput, 'TOutput>(mapper : ('TInput -> 'TOutput[]), array : 'TInput[]) : 'TOutput[] { + return Fold(PlusA<'TOutput>, new 'TOutput[0], Mapped(mapper, array)); + } + + /// # Summary + /// Given an array of arrays, returns the concatenation of all arrays. + /// + /// # Type Parameters + /// ## 'T + /// The type of `array` elements. + /// + /// # Input + /// ## arrays + /// Array of arrays. + /// + /// # Output + /// Concatenation of all arrays. + /// + /// # Example + /// ```Q# + /// let flattened = Flattened([[1, 2], [3], [4, 5, 6]]); + /// // flattened = [1, 2, 3, 4, 5, 6] + /// ``` + function Flattened<'T>(arrays : 'T[][]): 'T[] { + return Fold(PlusA<'T>, new 'T[0], arrays); + } + /// # Summary /// Given an array and an operation that is defined /// for the elements of the array, returns a new array that consists diff --git a/Standard/src/Arrays/Multidimensional.qs b/Standard/src/Arrays/Multidimensional.qs new file mode 100644 index 00000000000..7e2102585ab --- /dev/null +++ b/Standard/src/Arrays/Multidimensional.qs @@ -0,0 +1,276 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Microsoft.Quantum.Arrays { + open Microsoft.Quantum.Canon; + open Microsoft.Quantum.Convert; + open Microsoft.Quantum.Diagnostics; + open Microsoft.Quantum.Logical; + open Microsoft.Quantum.Math; + + /// # Summary + /// Returns the transpose of a matrix represented as an array + /// of arrays. + /// + /// # Description + /// Input as an $r \times c$ matrix with $r$ rows and $c$ columns. The matrix + /// is row-based, i.e., `matrix[i][j]` accesses the element at row $i$ and column $j$. + /// + /// This function returns the $c \times r$ matrix that is the transpose of the + /// input matrix. + /// + /// # Type Parameters + /// ## 'T + /// The type of each element of `matrix`. + /// + /// # Input + /// ## matrix + /// Row-based $r \times c$ matrix + /// + /// # Output + /// Transposed $c \times r$ matrix + /// + /// # Example + /// ```Q# + /// // same as [[1, 4], [2, 5], [3, 6]] + /// let transposed = Transposed([[1, 2, 3], [4, 5, 6]]); + /// ``` + function Transposed<'T>(matrix : 'T[][]) : 'T[][] { + let numRows = Length(matrix); + Fact(numRows > 0, "Matrix must have at least 1 row"); + let numColumns = Length(Head(matrix)); + Fact(numColumns > 0, "Matrix must have at least 1 column"); + RectangularArrayFact(matrix, "Matrix is not a rectangular array"); + + return Mapped(ColumnAtUnchecked(_, matrix), SequenceI(0, numColumns - 1)); + } + + /// # Summary + /// Returns the at the given index of an array. + /// + /// # Type Parameters + /// ## 'T + /// The type of each element of `array`. + /// + /// # Input + /// ## index + /// Index of element + /// ## array + /// The array being indexed. + /// + /// # Remark + /// This function is more general than `LookupFunction`, since + /// it can also be used for partial application on a fixed index. + /// Note that the type parameter must explicitly be provided in + /// this case as it cannot be deduced automatically. + /// + /// # Example + /// Get the third number in four famous integer sequences. (note + /// that the 0 index corresponds to the _first_ value of the sequence.) + /// ```Q# + /// let lucas = [2, 1, 3, 4, 7, 11, 18, 29, 47, 76]; + /// let prime = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]; + /// let fibonacci = [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]; + /// let catalan = [1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862]; + /// let famous2 = Mapped(ElementAt(2, _), [lucas, prime, fibonacci, catalan]); + /// // same as: famous2 = [3, 5, 1, 2] + /// ``` + /// + /// # See Also + /// - Microsoft.Quantum.Arrays.LookupFunction + /// - Microsoft.Quantum.Arrays.ElementsAt + function ElementAt<'T>(index : Int, array : 'T[]) : 'T { + Fact(index >= 0 and index < Length(array), "Index is out of bound"); + return array[index]; + } + + /// # Summary + /// Returns the array's elements at a given range + /// of indices. + /// + /// # Type Parameters + /// ## 'T + /// The type of each element of `array`. + /// + /// # Input + /// ## range + /// Range of array indexes + /// ## array + /// Array + /// + /// # Example + /// Get the odd indexes in famous integer sequences. (note + /// that the 0 index corresponds to the _first_ value of the sequence.) + /// ```Q# + /// let lucas = [2, 1, 3, 4, 7, 11, 18, 29, 47, 76]; + /// let prime = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]; + /// let fibonacci = [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]; + /// let catalan = [1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862]; + /// let famousOdd = Mapped(ElementsAt(0..2..9, _), [lucas, prime, fibonacci, catalan]); + /// // same as: famousOdd = [[2, 3, 7, 18, 47], [2, 5, 11, 17, 23], [0, 1, 3, 8, 21], [1, 2, 14, 132, 1430]] + /// ``` + /// + /// # See Also + /// - Microsoft.Quantum.Arrays.ElementAt + /// - Microsoft.Quantum.Arrays.LookupFunction + function ElementsAt<'T>(range : Range, array : 'T[]) : 'T[] { + return array[range]; + } + + /// # Summary + /// Extracts a column from a matrix. + /// + /// # Description + /// This function extracts a column in a matrix in row-wise order. + /// Extracting a row corrsponds to element access of the first index + /// and therefore requires no further treatment. + /// + /// # Type Parameters + /// ## 'T + /// The type of each element of `matrix`. + /// + /// # Input + /// ## column + /// Column of the matrix + /// ## matrix + /// 2-dimensional matrix in row-wise order + /// + /// # Example + /// ```Q# + /// let matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]; + /// let column = ColumnAt(0, matrix); + /// // same as: column = [1, 4, 7] + /// ``` + /// + /// # See Also + /// - Microsoft.Quantum.Arrays.Transposed + /// - Microsoft.Quantum.Arrays.Diagonal + function ColumnAt<'T>(column : Int, matrix : 'T[][]) : 'T[] { + RectangularArrayFact(matrix, "Matrix is not a rectangular array"); + return ColumnAtUnchecked(column, matrix); + } + + /// # Summary + /// This function does not check for matrix shape + /// + /// # Description + /// This function can be used in other multidimensional functions, + /// which already check the input matrix for a valid rectangular shape. + internal function ColumnAtUnchecked<'T>(column : Int, matrix : 'T[][]) : 'T[] { + return Mapped( + Compose( + ElementAt<'T>(column, _), + LookupFunction(matrix) + ), RangeAsIntArray(IndexRange(matrix))); + } + + /// # Summary + /// Returns an array of diagonal elements of a 2-dimensional array + /// + /// # Description + /// If the 2-dimensional array has not a square shape, the diagonal over + /// the minimum over the number of rows and columns will be returned. + /// + /// # Type Parameters + /// ## 'T + /// The type of each element of `matrix`. + /// + /// # Input + /// ## matrix + /// 2-dimensional matrix in row-wise order + /// + /// # Example + /// ```Q# + /// let matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]; + /// let diagonal = Diagonal(matrix); + /// // same as: column = [1, 5, 9] + /// ``` + /// + /// # See Also + /// - Microsoft.Quantum.Arrays.Transposed + function Diagonal<'T>(matrix : 'T[][]) : 'T[] { + RectangularArrayFact(matrix, "Matrix is not a rectangular array"); + + let numRows = Length(matrix); + let numColumns = numRows == 0 ? 0 | Length(Head(matrix)); + + return MappedOverRange(ElementAtDiagonal(_, matrix), 0..(MinI(numRows, numColumns) - 1)); + } + + internal function ElementAtDiagonal<'T>(index : Int, matrix : 'T[][]) : 'T { + return matrix[index][index]; + } + + /// # Summary + /// Represents a condition that a 2-dimensional array has a rectangular shape + /// + /// # Description + /// This function asserts that each row in an array has the same length. + /// + /// # Type Parameters + /// ## 'T + /// The type of each element of `array`. + /// + /// # Input + /// ## array + /// A 2-dimensional array of elements + /// ## message + /// A message to be printed if the array is not a rectangular array + /// + /// # Example + /// ```Q# + /// RectangularArrayFact([[1, 2], [3, 4]], "Array is not rectangular"); // okay + /// RectangularArrayFact([[1, 2, 3], [4, 5, 6]], "Array is not rectangular"); // okay + /// RectangularArrayFact([[1, 2], [3, 4, 5]], "Array is not rectangular"); // will fail + /// ``` + /// + /// # See Also + /// - Microsoft.Quantum.Arrays.SquareArrayFact + function RectangularArrayFact<'T>(array : 'T[][], message : String) : Unit { + if (Length(array) == 0) { + return (); + } else { + let numColumns = Length(Head(array)); + if (Any(Compose(NotEqualI(numColumns, _), Length<'T>), Rest(array))) { + fail message; + } + } + } + + /// # Summary + /// Represents a condition that a 2-dimensional array has a square shape + /// + /// # Description + /// This function asserts that each row in an array has + /// as many elements as there are rows (elements) in the array. + /// + /// # Type Parameters + /// ## 'T + /// The type of each element of `array`. + /// + /// # Input + /// ## array + /// A 2-dimensional array of elements + /// ## message + /// A message to be printed if the array is not a square array + /// + /// # Example + /// ```Q# + /// SquareArrayFact([[1, 2], [3, 4]], "Array is not a square"); // okay + /// SquareArrayFact([[1, 2, 3], [4, 5, 6]], "Array is not a square"); // will fail + /// SquareArrayFact([[1, 2], [3, 4, 5]], "Array is not a square"); // will fail + /// ``` + /// + /// # See Also + /// - Microsoft.Quantum.Arrays.RectangularArrayFact + function SquareArrayFact<'T>(array : 'T[][], message : String) : Unit { + if (Length(array) == 0) { + return (); + } else { + let numColumns = Length(array); + if (Any(Compose(NotEqualI(numColumns, _), Length<'T>), array)) { + fail message; + } + } + } +} diff --git a/Standard/src/Arrays/Reductions.qs b/Standard/src/Arrays/Reductions.qs new file mode 100644 index 00000000000..201c45b4fd7 --- /dev/null +++ b/Standard/src/Arrays/Reductions.qs @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Microsoft.Quantum.Arrays { + + /// # Summary + /// Combines Mapped and Fold into a single function + /// + /// # Description + /// This function iterates the `fn` function through the array, starting from + /// an initial state `state` and returns all intermediate values, not including + /// the inital state. + /// + /// # Type Parameters + /// ## 'State + /// The type of states that the `fn` function operates on, i.e., accepts as its first + /// input and returns. + /// ## 'T + /// The type of `array` elements. + /// + /// # Input + /// ## fn + /// A function to be folded over the array + /// + /// ## state + /// The initial state to be folded + /// + /// ## array + /// An array of values to be folded over + /// + /// # Output + /// All intermediate states, including the final state, but not the initial state. + /// The length of the output array is of the same length as `array`. + /// + /// # Remark + /// This function generalizes `Fold` since + /// `Tail(CumulativeFolded(fn, state, array))` is the same as `Fold(fn, state, array)`. + /// + /// # Example + /// ```Q# + /// // same as sums = [1, 3, 6, 10, 15] + /// let sums = CumulativeFolded(PlusI, 0, SequenceI(1, 5)); + /// ``` + function CumulativeFolded<'State, 'T>(fn : (('State, 'T) -> 'State), state : 'State, array : 'T[]) : 'State[] { + mutable current = state; + mutable result = new 'State[Length(array)]; + + for ((i, elem) in Enumerated(array)) { + set current = fn(current, elem); + set result w/= i <- current; + } + + return result; + } +} diff --git a/Standard/src/Arrays/Windows.qs b/Standard/src/Arrays/Windows.qs new file mode 100644 index 00000000000..bcad5c35111 --- /dev/null +++ b/Standard/src/Arrays/Windows.qs @@ -0,0 +1,76 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Microsoft.Quantum.Arrays { + + /// # Summary + /// Returns all consecutive subarrays of length `size`. + /// + /// # Description + /// This function returns all `n - size + 1` subarrays of + /// length `size` in order, where `n` is the length of `arr`. + /// The first subarrays are `arr[0..size - 1], arr[1..size], arr[2..size + 1]` + /// until the last subarray `arr[n - size..n - 1]`. + /// + /// If `size <= 0` or `size > n`, an empty array is returned. + /// + /// # Type Parameters + /// ## 'T + /// The type of `array` elements. + /// + /// # Input + /// ## size + /// Length of the subarrays. + /// + /// ## array + /// An array of elements. + /// + /// # Example + /// ```Q# + /// // same as [[1, 2, 3], [2, 3, 4], [3, 4, 5]] + /// let windows = Windows(3, [1, 2, 3, 4, 5]); + /// ``` + function Windows<'T>(size : Int, array : 'T[]) : 'T[][] { + let n = Length(array); + + if (size <= 0 or size > n) { + return new 'T[][0]; + } + + mutable result = new 'T[][n + 1 - size]; + + for (i in 0..n - size) { + set result w/= i <- array[i..i + size - 1]; + } + + return result; + } + + /// # Summary + /// Given an array, returns all its prefixes. + /// + /// # Description + /// Returns an array of all prefixes, starting with an array that only + /// has the first element until the complete array. + /// + /// # Type Parameters + /// ## 'T + /// The type of `array` elements. + /// + /// # Input + /// ## array + /// An array of elements. + /// + /// # Example + /// ```Q# + /// let prefixes = Prefixes([23, 42, 144]); + /// // prefixes = [[23], [23, 42], [23, 42, 144]] + /// ``` + function Prefixes<'T>(array : 'T[]) : 'T[][] { + return MappedOverRange(Prefix(_, array), IndexRange(array)); + } + + internal function Prefix<'T>(to : Int, array : 'T[]) : 'T[] { + return array[0..to]; + } +} diff --git a/Standard/src/Arrays/Zip.qs b/Standard/src/Arrays/Zip.qs index caa785a2ba5..f698d9ffea9 100644 --- a/Standard/src/Arrays/Zip.qs +++ b/Standard/src/Arrays/Zip.qs @@ -36,6 +36,7 @@ namespace Microsoft.Quantum.Arrays { /// # See Also /// - Zip3 /// - Zip4 + /// - Unzipped function Zip<'T, 'U> (left : 'T[], right : 'U[]) : ('T, 'U)[] { let nElements = Length(left) < Length(right) ? Length(left) @@ -131,6 +132,46 @@ namespace Microsoft.Quantum.Arrays { return output; } + /// # Summary + /// Given an array of 2-tuples, returns a tuple of two arrays, each containing + /// the elements of the tuples of the input array. + /// + /// # Type Parameters + /// ## 'T + /// The type of the first element in each tuple + /// ## 'U + /// The type of the second element in each tuple + /// + /// # Input + /// ## arr + /// An array containing 2-tuples + /// + /// # Output + /// Two arrays, the first one containing all first elements of the input + /// tuples, the second one containing all second elements of the input tuples. + /// + /// # Example + /// ```Q# + /// // split is same as ([6, 5, 5, 3, 2, 1], [true, false, false, false, true, false]) + /// let split = Unzipped([(6, true), (5, false), (5, false), (3, false), (2, true), (1, false)]); + /// ``` + /// + /// # Remark + /// This function is equivalent to `(Mapped(Fst<'T, 'U>, arr), Mapped(Snd<'T, 'U>, arr))`. + /// + /// # See Also + /// - Zip + function Unzipped<'T, 'U>(arr : ('T, 'U)[]) : ('T[], 'U[]) { + let nElements = Length(arr); + mutable first = new 'T[nElements]; + mutable second = new 'U[nElements]; + for (idxElement in 0 .. nElements - 1) { + let (left, right) = arr[idxElement]; + set first w/= idxElement <- left; + set second w/= idxElement <- right; + } + return (first, second); + } } diff --git a/Standard/tests/ArrayTests.qs b/Standard/tests/ArrayTests.qs index 5dbadf4d724..bd674b65b50 100644 --- a/Standard/tests/ArrayTests.qs +++ b/Standard/tests/ArrayTests.qs @@ -1,14 +1,15 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace Microsoft.Quantum.Tests { - open Microsoft.Quantum.Logical; - open Microsoft.Quantum.Diagnostics; + open Microsoft.Quantum.Arrays; open Microsoft.Quantum.Canon; + open Microsoft.Quantum.Diagnostics; open Microsoft.Quantum.Intrinsic; - open Microsoft.Quantum.Arrays; + open Microsoft.Quantum.Logical; + open Microsoft.Quantum.Math; @Test("QuantumSimulator") - function ZipTest() : Unit { + function TestZip() : Unit { let left = [1, 2, 101]; let right = [PauliY, PauliI]; @@ -26,9 +27,19 @@ namespace Microsoft.Quantum.Tests { } } + @Test("QuantumSimulator") + function TestUnzipped() : Unit { + let first = [6, 5, 5, 3, 2, 1]; + let second = [true, false, false, false, true, false]; + + let (first2, second2) = Unzipped(Zip(first, second)); + AllEqualityFactI(first2, first, "Unexpected array of integers"); + AllEqualityFactB(second2, second, "Unexpected array of Booleans"); + } + @Test("QuantumSimulator") - function LookupTest () : Unit { + function TestLookup() : Unit { let array = [1, 12, 71, 103]; let fn = LookupFunction(array); @@ -45,7 +56,7 @@ namespace Microsoft.Quantum.Tests { } @Test("QuantumSimulator") - function ChunksTest() : Unit { + function TestChunks() : Unit { let data = [10, 11, 12, 13, 14, 15]; // 2 × 3 case. @@ -98,6 +109,14 @@ namespace Microsoft.Quantum.Tests { EqualityFactB(All(IsEven, evenArray), true, $"the even elements of [1..10] were not correctly filtered."); } + @Test("QuantumSimulator") + function TestCount() : Unit { + + let array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + let countEvens = Count(IsEven, array); + EqualityFactI(countEvens, 5, $"the even elements of [1..10] were not correctly counted."); + } + function ReverseTest () : Unit { @@ -237,6 +256,111 @@ namespace Microsoft.Quantum.Tests { let differentElements = EqualA(EqualR, [One, Zero], [One, One]); Fact(not differentElements, "Arrays with different elements were reported as equal"); } + + @Test("QuantumSimulator") + operation TestInterleaved() : Unit { + AllEqualityFactI(Interleaved([1, 2, 3], [-1, -2, -3]), [1, -1, 2, -2, 3, -3], "Interleaving failed"); + AllEqualityFactB(Interleaved(ConstantArray(3, false), ConstantArray(2, true)), [false, true, false, true, false], "Interleaving failed"); + } + + @Test("QuantumSimulator") + operation TestCumulativeFolded() : Unit { + AllEqualityFactI(CumulativeFolded(PlusI, 0, SequenceI(1, 5)), [1, 3, 6, 10, 15], "CumulativeFolded failed"); + } + + @Test("QuantumSimulator") + operation TestTransposed() : Unit { + for ((actual, expected) in Zip(Transposed([[1, 2, 3], [4, 5, 6]]), [[1, 4], [2, 5], [3, 6]])) { + AllEqualityFactI(actual, expected, "Transposed failed"); + } + } + + @Test("QuantumSimulator") + operation TestColumnAt() : Unit { + let matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]; + AllEqualityFactI(ColumnAt(0, matrix), [1, 4, 7], "ColumnAt failed"); + AllEqualityFactI(ColumnAt(1, matrix), [2, 5, 8], "ColumnAt failed"); + AllEqualityFactI(ColumnAt(2, matrix), [3, 6, 9], "ColumnAt failed"); + } + + @Test("QuantumSimulator") + operation TestElementAt() : Unit { + let lucas = [2, 1, 3, 4, 7, 11, 18, 29, 47, 76]; + let prime = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]; + let fibonacci = [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]; + let catalan = [1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862]; + let famous2 = Mapped(ElementAt(2, _), [lucas, prime, fibonacci, catalan]); + AllEqualityFactI(famous2, [3, 5, 1, 2], "ElementAt failed"); + } + + @Test("QuantumSimulator") + operation TestElementsAt() : Unit { + let lucas = [2, 1, 3, 4, 7, 11, 18, 29, 47, 76]; + let prime = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]; + let fibonacci = [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]; + let catalan = [1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862]; + let famousOdd = Mapped(ElementsAt(0..2..9, _), [lucas, prime, fibonacci, catalan]); + for ((actual, expected) in Zip(famousOdd, [[2, 3, 7, 18, 47], [2, 5, 11, 17, 23], [0, 1, 3, 8, 21], [1, 2, 14, 132, 1430]])) { + AllEqualityFactI(actual, expected, "ElementsAt failed"); + } + } + + @Test("QuantumSimulator") + operation TestDiagonal() : Unit { + AllEqualityFactI(Diagonal([[1, 2, 3], [4, 5, 6], [7, 8, 9]]), [1, 5, 9], "Diagonal failed"); + AllEqualityFactI(Diagonal([[1, 2, 3], [4, 5, 6]]), [1, 5], "Diagonal failed"); + AllEqualityFactI(Diagonal([[1, 2], [4, 5], [7, 8]]), [1, 5], "Diagonal failed"); + } + + @Test("QuantumSimulator") + operation TestWindows() : Unit { + let EqualIntA = EqualA(EqualI, _, _); + let EqualIntAA = EqualA(EqualIntA, _, _); + + Fact(EqualIntAA(Windows(-1, [1, 2, 3]), new Int[][0]), "unexpected windows"); + Fact(EqualIntAA(Windows(0, [1, 2, 3]), new Int[][0]), "unexpected windows"); + Fact(EqualIntAA(Windows(1, [1, 2, 3]), [[1], [2], [3]]), "unexpected windows"); + Fact(EqualIntAA(Windows(2, [1, 2, 3]), [[1, 2], [2, 3]]), "unexpected windows"); + Fact(EqualIntAA(Windows(3, [1, 2, 3]), [[1, 2, 3]]), "unexpected windows"); + Fact(EqualIntAA(Windows(4, [1, 2, 3]), new Int[][0]), "unexpected windows"); + } + + @Test("QuantumSimulator") + operation TestPrefixes() : Unit { + let array = [0, 1, 1, 2, 3, 5]; + let prefixes = Prefixes(array); + + EqualityFactI(Length(prefixes), Length(array), "unexpected length for prefixes"); + AllEqualityFactI(prefixes[0], [0], "unexpected prefix"); + AllEqualityFactI(prefixes[1], [0, 1], "unexpected prefix"); + AllEqualityFactI(prefixes[2], [0, 1, 1], "unexpected prefix"); + AllEqualityFactI(prefixes[3], [0, 1, 1, 2], "unexpected prefix"); + AllEqualityFactI(prefixes[4], [0, 1, 1, 2, 3], "unexpected prefix"); + AllEqualityFactI(prefixes[5], [0, 1, 1, 2, 3, 5], "unexpected prefix"); + } + + @Test("QuantumSimulator") + operation TestSuccessfulRectangularFact() : Unit { + RectangularArrayFact([[1, 2], [3, 4]], "Array is not rectangular"); + RectangularArrayFact([[1, 2, 3], [4, 5, 6]], "Array is not rectangular"); + } + + operation RectangularFactTestShouldFail() : Unit { + RectangularArrayFact([[1, 2], [3, 4, 5]], "Array is not rectangular"); + } + + @Test("QuantumSimulator") + operation TestSuccessfulSquareFact() : Unit { + SquareArrayFact([[1, 2], [3, 4]], "Array is not a square"); + } + + operation SquareFact1TestShouldFail() : Unit { + SquareArrayFact([[1, 2, 3], [4, 5, 6]], "Array is not a square"); + } + + operation SquareFact2TestShouldFail() : Unit { + SquareArrayFact([[1, 2], [3, 4, 5]], "Array is not a square"); + } } diff --git a/Standard/tests/EnumerationTests.qs b/Standard/tests/EnumerationTests.qs index 48e23694f9d..5e9db499f26 100644 --- a/Standard/tests/EnumerationTests.qs +++ b/Standard/tests/EnumerationTests.qs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace Microsoft.Quantum.Tests { open Microsoft.Quantum.Math; @@ -59,6 +59,33 @@ namespace Microsoft.Quantum.Tests { EqualityFactI(Fold(Add, 0, squaredArray), 30, $"the sum of the squares of [1, 2, 3, 4] was not found to be 30."); } + @Test("QuantumSimulator") + function TestMappedOverNonEmptyRange() : Unit { + AllEqualityFactI(MappedOverRange(PlusI(_, 2), 1..5), [3, 4, 5, 6, 7], "MappedOverRange failed."); + } + + @Test("QuantumSimulator") + function TestMappedOverReversedRange() : Unit { + AllEqualityFactI(MappedOverRange(TimesI(_, 2), 4..-2..-4), [8, 4, 0, -4, -8], "MappedOverRange failed."); + } + + @Test("QuantumSimulator") + function TestMappedOverEmpty() : Unit { + AllEqualityFactI(MappedOverRange(TimesI(_, 2), 1..-1..2), new Int[0], "MappedOverRange failed."); + } + + @Test("QuantumSimulator") + function TestFlatMapped() : Unit { + let numbers = FlatMapped(SequenceI(1, _), SequenceI(1, 5)); + AllEqualityFactI(numbers, [1, 1, 2, 1, 2, 3, 1, 2, 3, 4, 1, 2, 3, 4, 5], "FlatMapped failed"); + } + + @Test("QuantumSimulator") + function TestFlattened() : Unit { + let numbers = Flattened(ConstantArray(3, SequenceI(1, 3))); + AllEqualityFactI(numbers, [1, 2, 3, 1, 2, 3, 1, 2, 3], "Flattened failed"); + } + function ExtremaTest () : Unit { let array = [-10, 10, 7, 0]; EqualityFactI(-10, Min(array), $"Min failed.");