Open cgranade opened 3 years ago
Edited to reflect recent changes in https://github.com/microsoft/qsharp-language/pull/49.
Thanks for the proposal, @cgranade. Here are some comments:
Reshaped2To3
and Reshaped3To3
Size
for consistency with Size2
and Size3
. Would this function be identical to Length
?Thanks for the proposal, @cgranade. Here are some comments:
- The return type seems broken in
Reshaped2To3
andReshaped3To3
Thanks for catching that typo!
- Will there be a function called
Size
for consistency withSize2
andSize3
. Would this function be identical toLength
?
I'm somewhat reticent to introduce another function that is identical to Length
but that has a different name; perhaps it would be worth renaming Length
instead?
Library support for multidimensional arrays
Abstract and conceptual overview
Q# currently provides the array type
'T[]
for any type'T
. By using arrays of arrays (i.e.:'T[][]
), Q# allows for representing multidimensional data. As discussed at https://github.com/microsoft/qsharp-language/pull/49, however, using nested arrays in this way presents a number of limitations.This proposal builds on the work in https://github.com/microsoft/qsharp-language/pull/49 by providing a number of different library functions and operations to support the new multidimensional arrays in the corresponding Q# language proposal. For details on the motivation, justification, and context for both proposals, please see https://github.com/microsoft/qsharp-language/pull/49.
Proposal
New and modified functions and operations
This proposal introduces new functions and operations for creating, manipulating, and transforming multidimensional arrays (i.e.: values of type
[|'T|]
,[||'T||]
, and so forth), extending the Microsoft.Quantum.Array namespace to support the new types introduced by https://github.com/microsoft/qsharp-language/pull/49. To distinguish functions acting on multidimensional arrays of distinct ranks, suffixes are used to denote the rank each function or operation acts on (e.g.:EmptyArray2
returns a value of type[|'T|]
whileEmptyArray3
returns a value of type[||'T||]
). For brevity, we list only the rank-1, rank-2, and rank-3 variants of each proposed addition here, but the pattern should be continued to include multidimensional arrays of rank up to and including 8. For details on how to remove these suffixes using bounded polymorphism, please see "Relationship to Q# language feature proposals," below.As an implementation detail, all of the functions and operations proposed here can be written in pure Q#, using the functions described in https://github.com/microsoft/qsharp-language/pull/49 to create and deconstruct multidimensional arrays from their underlying strides, offsets, and sizes.
namespace Microsoft.Quantum.Arrays
function Concatenated<'T>(left : 'T[], right : 'T[]) : ['T]
function Concatenated2<'T>(axis : Int, left : [|'T|], right : [|'T|]) : [|'T|]
function Concatenated3<'T>(axis : Int, left : [||'T||], right : [||'T||]) : [||'T||]
[[1], [2, 3]]
).function JaggedAsRectangular2<'T>(jagged : [['T]]) : [|'T|]
function JaggedAsRectangular3<'T>(jagged : [[['T]]]) : [||'T||]
DiagonalArray2(0, [1, 2]))
returns[|1, 0|, |0, 2|]
).function DiagonalArray2<'T>(zero : 'T, diag : 'T[]) : [|'T|]
function DiagonalArray3<'T>(zero : 'T, diag : 'T[]) : [||'T||]
ConstantArray
function to apply to multidimensional arrays.function ConstantArray2<'T>(size : (Int, Int), value : 'T) : [|'T|]
function ConstantArray3<'T>(size : (Int, Int, Int), value : 'T) : [||'T||]
function EmptyArray2<'T>() : [|'T|]
function EmptyArray3<'T>() : [||'T||]
function Size2<'T>(array : [|'T|]) : (Int, Int)
function Size3<'T>(array : [||'T||]) : (Int, Int, Int)
function ElementAt2<'T>(index : (Int, Int), array : [|'T|]) : 'T
function ElementAt3<'T>(index : (Int, Int, Int), array : [||'T||]) : 'T
[arr[(0, 0, 0)], arr[(1, 1, 1)], arr[(2, 2, 2)], ...]
).function Diagonal2<'T>(array : [|'T|]) : ['T]
function Diagonal3<'T>(array : [||'T||]) : ['T]
function Subarray2<'T>(indices : (Int, Int)[], array : [|'T|]) : ['T]
function Subarray3<'T>(indices : (Int, Int, Int)[], array : [||'T||]) : ['T]
Fold
by computing folds along each axis of a multidimensional array.function FoldedAlongAxis2<'TInput, 'TOutput>(reduction : (['TInput] -> 'TOutput), axis : Int, array : [|'TInput|]) : ['TOutput]
function FoldedAlongAxis3<'TInput, 'TOutput>(reduction : (['TInput] -> 'TOutput), axis : Int, array : [||'TInput||]) : [|'TOutput|]
function TensorDot2With2<'T>(plus : (('T, 'T) -> 'T), times : (('T, 'T) -> 'T), (idxLeft : Int, idxRight : Int), left : [|'T|], right : [|'T|]) : [|'T|]
function TensorDot3With2<'T>(plus : (('T, 'T) -> 'T), times : (('T, 'T) -> 'T), (idxLeft : Int, idxRight : Int), left : [||'T||], right : [|'T|]) : [||'T||]
function TensorDot2With3<'T>(plus : (('T, 'T) -> 'T), times : (('T, 'T) -> 'T), (idxLeft : Int, idxRight : Int), left : [|'T|], right : [||'T||]) : [||'T||]
function TensorDot3With3<'T>(plus : (('T, 'T) -> 'T), times : (('T, 'T) -> 'T), (idxLeft : Int, idxRight : Int), left : [||'T||], right : [||'T||]) : [|||'T|||]
Double
orComplex
elements)function Dot11D(left : [Double], right : [Double]) : Double
function Dot11C(left : [Complex], right : [Complex]) : Complex
function Dot12D(left : [Double], right : [|Double|]) : [Double]
function Dot21D(left : [|Double|], right : [Double]) : [Double]
function Dot22D(left : [|Double|], right : [|Double|]) : [|Double|]
function Dagger(array : [|Complex|]) : [|Complex|]
function Elementwise2<'TLeft, 'TRight, 'TOutput>(fn : (('TLeft, 'TRight) -> 'TOutput)) : (([|'TLeft|], [|'TRight|]) -> [|'TOutput|])
For example:
Elementwise2(TimesD)(left, right)
returns the elementwise product of each element in left and right.function Elementwise3<'TLeft, 'TRight, 'TOutput>(fn : (('TLeft, 'TRight) -> 'TOutput)) : (([||'TLeft||], [||'TRight||]) -> [||'TOutput||])
function Reshaped1To2<'T>(array : ['T], newShape : (Int, Int)) : [|'T|]
function Reshaped1To3<'T>(array : ['T], newShape : (Int, Int, Int)) : [||'T||]
function Reshaped2To3<'T>(array : [|'T|], newShape : (Int, Int, Int)) :[|| 'T||,]
function Reshaped3To3<'T>(array : [||'T||], newShape : (Int, Int, Int)) :[|| 'T||,]
function Reshaped3To2<'T>(array : [||'T||], newShape : (Int, Int)) : [|'T|]
function Broadcasted2<'T0, 'T1>(in0 : 'T0[,], in1 : 'T1[,]) : ('T0[,], 'T1[,])
function Broadcasted3<'T0, 'T1>(in0 : 'T0[,,], in1 : 'T1[,,]) : ('T0[,,], 'T1[,,])
function Transposed2<'T>(data : [|'T|]) : [|'T|]
function Transposed3<'T>(axes : (Int, Int, Int), data : [||'T||]) : [||'T||]
function Transposed4<'T>(axes : (Int, Int, Int, Int), data : [|||'T|||]) : [|||'T|||]
Note that the list above does not include a few functions common in other frameworks, deferring to language Q# support in such cases (e.g.:
np.ones((3, 3))
can be replaced by[| 1.0, size=(3, 3) |]
).Examples
TODO
Relationship to Q# language feature proposals
Bounded polymorphism (microsoft/qsharp-language#149)
The use of rank suffixes could be largely eliminated by using bounded polymorphism to represent common patterns, such as indexing and size.
For example:
Open design questions and considerations
2
and3
are also used to indicate the number of inputs, as inZipped2
; how can we disambiguate these two uses?