You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
New content: Add definition for shape broadcasting
This change introduces a new section for Algorithms, following APIs,
to collect algorithms referenced throughout the specification.
A section for Broadcasting is introduced, which defines broadcasting
shapes and gives an explicit algorithm matching WebNN implementations
of NumPy's General Broadcasting Rules. Definitions for "broadcastable"
and "unidirectionally broadcastable" are introduced. The previous
definition of "broadcast-shapes" is removed in favor of these new
algorithms.
Use broadcasting definition in expand(), rather than bespoke steps
For webmachinelearning#324, webmachinelearning#378, webmachinelearning#462, and potentially webmachinelearning#523.
Co-authored-by: Dwayne Robinson <[email protected]>
1. If |a|.{{MLOperand/[[descriptor]]}}.{{MLOperandDescriptor/dataType}} is not equal to |b|.{{MLOperand/[[descriptor]]}}.{{MLOperandDescriptor/dataType}}, then [=exception/throw=] a "{{DataError}}" {{DOMException}}.
2241
2241
1. Let |descriptor| be a new {{MLOperandDescriptor}}.
2242
2242
1. Set |descriptor|.{{MLOperandDescriptor/dataType}} to |a|.{{MLOperand/[[descriptor]]}}.{{MLOperandDescriptor/dataType}}.
2243
-
1. Set |descriptor|.{{MLOperandDescriptor/dimensions}} to the result of running the [=MLGraphBuilder/broadcast-shapes=] steps given |a|.{{MLOperand/[[descriptor]]}}.{{MLOperandDescriptor/dimensions}} and |b|.{{MLOperand/[[descriptor]]}}.{{MLOperandDescriptor/dimensions}}.
2244
-
1. If that [=exception/throws=] an error, re-[=exception/throw=]the error.
2243
+
1. Set |descriptor|.{{MLOperandDescriptor/dimensions}} to the result of [=bidirectionally broadcasting the shapes=] |a|.{{MLOperand/[[descriptor]]}}.{{MLOperandDescriptor/dimensions}} and |b|.{{MLOperand/[[descriptor]]}}.{{MLOperandDescriptor/dimensions}}.
2244
+
1. If that returns failure, then [=exception/throw=]a "{{DataError}}" {{DOMException}}.
2245
2245
1. If any of the following sub-steps fail, [=exception/throw=] an "{{OperationError}}" {{DOMException}}.
2246
2246
1. Let |output| be the result of [=creating an MLOperand=] given [=this=] and |descriptor|.
To <dfn for="MLGraphBuilder">broadcast-shapes</dfn> given [=/list=] |shape1| and [=/list=] |shape2|, run the following steps:
2261
-
</summary>
2262
-
<div class=algorithm-steps>
2263
-
1. [=Assert=]: The type of |shape1| and |shape2| is `sequence of unsigned long`.
2264
-
1. Let |output| be the result of invoking the [=implementation-defined=] shape broadcast on |shape1| and |shape2|.
2265
-
1. If that fails, then [=exception/throw=] a "{{DataError}}" {{DOMException}}.
2266
-
1. Return |output|.
2267
-
<div class = "note">
2268
-
The most common implementation is that two shapes are compatible, when each of their corresponding dimensions are equal, or one of them is 1. The output shape consists of the maximum of the corresponding dimensions.
2269
-
</div>
2270
-
</div>
2271
-
</details>
2272
-
2273
2258
<details open>
2274
2259
<summary>
2275
2260
The element-wise binary operation algorithms invoke the [=MLGraphBuilder/element-wise-binary-op | create element-wise binary operation=] steps as follows.
@@ -2376,8 +2361,8 @@ Although operations *greaterOrEqual* and *lesserOrEqual* can each be implemented
2376
2361
1. If |a|.{{MLOperand/[[descriptor]]}}.{{MLOperandDescriptor/dataType}} is not equal to |b|.{{MLOperand/[[descriptor]]}}.{{MLOperandDescriptor/dataType}}, then [=exception/throw=] a "{{DataError}}" {{DOMException}}.
2377
2362
1. Let |descriptor| be a new {{MLOperandDescriptor}}.
2378
2363
1. Set |descriptor|.{{MLOperandDescriptor/dataType}} to {{MLOperandDataType/"uint8"}}.
2379
-
1. Set |descriptor|.{{MLOperandDescriptor/dimensions}} to the result of running the [=MLGraphBuilder/broadcast-shapes=] steps given |a|.{{MLOperand/[[descriptor]]}}.{{MLOperandDescriptor/dimensions}} and |b|.{{MLOperand/[[descriptor]]}}.{{MLOperandDescriptor/dimensions}}.
2380
-
1. If that [=exception/throws=] an error, re-[=exception/throw=]the error.
2364
+
1. Set |descriptor|.{{MLOperandDescriptor/dimensions}} to the result of [=bidirectionally broadcasting the shapes=] |a|.{{MLOperand/[[descriptor]]}}.{{MLOperandDescriptor/dimensions}} and |b|.{{MLOperand/[[descriptor]]}}.{{MLOperandDescriptor/dimensions}}.
2365
+
1. If that returns failure, then [=exception/throw=]a "{{DataError}}" {{DOMException}}.
2381
2366
1. If any of the following sub-steps fail, [=exception/throw=] an "{{OperationError}}" {{DOMException}}.
2382
2367
1. Let |output| be the result of [=creating an MLOperand=] given [=this=] and |descriptor|.
The permissions and context validity have been checked by [[#api-mlgraphbuilder-constructor]] steps.
2712
2697
</div>
2713
-
1. If any of the following steps fail, then [=exception/throw=] a "{{DataError}}" {{DOMException}}.
2714
-
1. Let |inputDesc| be |input|.{{MLOperand/[[descriptor]]}}.
2715
-
1. If the sequence length of |newShape| is not equal to the [=rank=] of |inputDesc|, then [=exception/throw=] a "{{DataError}}" {{DOMException}}.
2716
-
1. Let |outputDesc| be a copy of |inputDesc|.
2717
-
1. [=list/For each=] |index| in [=the range=] 0 to the [=rank=] of |input|, exclusive:
2718
-
1. Let |size| be the |input|.{{MLOperand/shape()}}[|index|].
2719
-
1. If |size| is not equal to 1 and not equal to |newShape|[index], then [=exception/throw=] a "{{DataError}}" {{DOMException}}.
2720
-
1. If |size| is equal to 1, then let |outputDesc|.{{MLOperandDescriptor/dimensions}}[|index|] be |newShape|[|index|].
2698
+
1. Let |descriptor| be a new {{MLOperandDescriptor}}.
2699
+
1. Set |descriptor|.{{MLOperandDescriptor/dataType}} to |input|.{{MLOperand/[[descriptor]]}}.{{MLOperandDescriptor/dataType}}.
2700
+
1. Set |outputDescriptor|.{{MLOperandDescriptor/dimensions}} to the result of [=unidirectionally broadcasting the shapes=] |input|.{{MLOperand/[[descriptor]]}}.{{MLOperandDescriptor/dimensions}} and |newShape|.
2701
+
1. If that returns failure, then [=exception/throw=] a "{{DataError}}" {{DOMException}}.
2721
2702
1. If any of the following sub-steps fail, [=exception/throw=] an "{{OperationError}}" {{DOMException}}.
2722
-
1. Let |output| be the result of [=creating an MLOperand=] given [=this=] and |outputDesc|.
2703
+
1. Let |output| be the result of [=creating an MLOperand=] given [=this=] and |outputDescriptor|.
2723
2704
1. Make a request to the underlying platform to:
2724
2705
1. Create [=platform operator=] |expandImpl| for this method, given |input| and |newShape|.
2725
2706
1. Set |output|.{{MLOperand/[[operator]]}} to |expandImpl|.
Calculate the [general matrix multiplication of the Basic Linear Algebra Subprograms](https://en.wikipedia.org/wiki/Basic_Linear_Algebra_Subprograms#Level_3). The calculation follows the expression `alpha * A * B + beta * C`, where `A` is a 2-D tensor with shape [M, K] or [K, M], `B` is a 2-D tensor with shape [K, N] or [N, K], and `C` is broadcastable to the shape [M, N]. `A` and `B` may optionally be transposed prior to the calculation.
2846
+
Calculate the [general matrix multiplication of the Basic Linear Algebra Subprograms](https://en.wikipedia.org/wiki/Basic_Linear_Algebra_Subprograms#Level_3). The calculation follows the expression `alpha * A * B + beta * C`, where `A` is a 2-D tensor with shape [M, K] or [K, M], `B` is a 2-D tensor with shape [K, N] or [N, K], and `C` is [=unidirectionally broadcastable=] to the shape [M, N]. `A` and `B` may optionally be transposed prior to the calculation.
The third input tensor. It is either a scalar, or of the shape that is unidirectionally broadcastable to the shape [M, N] according to [[!numpy-broadcasting-rule]]. When it is not specified, the computation is done as if *c* is a scalar 0.0.
2866
+
The third input tensor. It is either a scalar, or of the shape that is [=unidirectionally broadcastable=] to the shape [M, N]. When it is not specified, the computation is done as if *c* is a scalar 0.0.
1. If |options|.{{MLGemmOptions/aTranspose}} is true, then let |shapeA| be the reverse array of |shapeA|.
2923
2904
1. If |options|.{{MLGemmOptions/bTranspose}} is true, then let |shapeB| be the reverse array of |shapeB|.
2924
2905
1. If |shapeA|[1] is not equal to |shapeB|[0], then [=exception/throw=] a "{{DataError}}" {{DOMException}}.
2925
-
1. If |options|.{{MLGemmOptions/c}}[=map/exists=] and is not unidirectionally broadcastable to the shape [|shapeA|[0], |shapeB|[1]] according to the [[!numpy-broadcasting-rule]], then [=exception/throw=] a "{{DataError}}" {{DOMException}}.
2906
+
1. If |options|.{{MLGemmOptions/c}}[=map/exists=] and is not [=unidirectionally broadcastable=] to the shape [|shapeA|[0], |shapeB|[1]], then [=exception/throw=] a "{{DataError}}" {{DOMException}}.
2926
2907
<div class="note">
2927
2908
Type compatibility between |a|, |b| and |options|.{{MLGemmOptions/c}} can be also checked.
The <dfn method for=MLGraphBuilder>layerNormalization(|input|, |options|)</dfn> method steps are:
3677
3658
</summary>
3678
3659
<div class=algorithm-steps>
3679
-
1. If |options|.{{MLLayerNormalizationOptions/axes}} does not [=map/exist=], then set |options|.{{MLLayerNormalizationOptions/axes}} to a new [=/list=], either equal to [=the range=] from 1 to |input|'s [=rank-=], exclusive, if |input|'s [=rank=] is greater than 1, or an empty [=/list=] otherwise.
3660
+
1. If |options|.{{MLLayerNormalizationOptions/axes}} does not [=map/exist=], then set |options|.{{MLLayerNormalizationOptions/axes}} to a new [=/list=], either equal to [=the range=] from 1 to |input|'s [=rank=], exclusive, if |input|'s [=rank=] is greater than 1, or an empty [=/list=] otherwise.
3680
3661
1. If the [=rank=] of |options|.{{MLLayerNormalizationOptions/scale}} is not equal to the [=list/size=] of |options|.{{MLLayerNormalizationOptions/axes}}, then [=exception/throw=] a "{{DataError}}" {{DOMException}}.
3681
3662
1. If the [=rank=] of |options|.{{MLLayerNormalizationOptions/bias}} is not equal to the [=list/size=] of |options|.{{MLLayerNormalizationOptions/axes}}, then [=exception/throw=] a "{{DataError}}" {{DOMException}}.
3682
3663
1. [=list/For each=] |index| in [=the range=] 0 to the [=list/size=] of |options|.{{MLLayerNormalizationOptions/axes}}, exclusive:
- *slope*: an {{MLOperand}}. The slope tensor. Its shape is either the same as, or unidirectionally broadcastable to the shape of input tensor *input* according to [[!numpy-broadcasting-rule]].
4707
+
- *slope*: an {{MLOperand}}. The slope tensor. Its shape is either the same as, or [=unidirectionally broadcastable=] to the shape of input tensor *input*.
4727
4708
4728
4709
**Returns:**
4729
4710
- an {{MLOperand}}. The output tensor of the same shape as *input*.
1. Let |descriptor| be a new {{MLOperandDescriptor}}.
4739
4720
1. Set |descriptor|.{{MLOperandDescriptor/dataType}} to |input|.{{MLOperand/[[descriptor]]}}.{{MLOperandDescriptor/dataType}}.
4740
-
1. Set |descriptor|.{{MLOperandDescriptor/dimensions}} to the result of running the [=MLGraphBuilder/broadcast-shapes=] steps given |input|.{{MLOperand/[[descriptor]]}}.{{MLOperandDescriptor/dimensions}} and |slope|.{{MLOperand/[[descriptor]]}}.{{MLOperandDescriptor/dimensions}}.
4741
-
1. If that [=exception/throws=] an error, re-[=exception/throw=]the error.
4721
+
1. Set |descriptor|.{{MLOperandDescriptor/dimensions}} to the result of [=unidirectionally broadcasting the shapes=] |input|.{{MLOperand/[[descriptor]]}}.{{MLOperandDescriptor/dimensions}} and |slope|.{{MLOperand/[[descriptor]]}}.{{MLOperandDescriptor/dimensions}}.
4722
+
1. If that returns failure, then [=exception/throw=]a "{{DataError}}" {{DOMException}}.
4742
4723
1. If any of the following sub-steps fail, [=exception/throw=] an "{{OperationError}}" {{DOMException}}.
4743
4724
1. Let |output| be the result of [=creating an MLOperand=] given [=this=] and |descriptor|.
1. If |input|.{{MLOperand/[[descriptor]]}}.{{MLOperandDescriptor/dataType}} is not equal to |other|.{{MLOperand/[[descriptor]]}}.{{MLOperandDescriptor/dataType}}, then [=exception/throw=] a "{{DataError}}" {{DOMException}}.
5872
5853
1. Let |descriptor| be a new {{MLOperandDescriptor}}.
5873
5854
1. Set |descriptor|.{{MLOperandDescriptor/dataType}} to |input|.{{MLOperand/[[descriptor]]}}.{{MLOperandDescriptor/dataType}}.
5874
-
1. Set |descriptor|.{{MLOperandDescriptor/dimensions}} to the result of running the [=MLGraphBuilder/broadcast-shapes=] steps given |input|.{{MLOperand/[[descriptor]]}}.{{MLOperandDescriptor/dimensions}} and |other|.{{MLOperand/[[descriptor]]}}.{{MLOperandDescriptor/dimensions}}.
5875
-
1. If that [=exception/throws=] an error, re-[=exception/throw=]the error.
5876
-
1. If |condition| is not unidirectionally broadcastable to |descriptor|.{{MLOperandDescriptor/dimensions}} according to the [[!numpy-broadcasting-rule]], then [=exception/throw=] a "{{DataError}}" {{DOMException}}.
5855
+
1. Set |descriptor|.{{MLOperandDescriptor/dimensions}} to the result of [=bidirectionally broadcasting the shapes=] |input|.{{MLOperand/[[descriptor]]}}.{{MLOperandDescriptor/dimensions}} and |other|.{{MLOperand/[[descriptor]]}}.{{MLOperandDescriptor/dimensions}}.
5856
+
1. If that returns failure, then [=exception/throw=]a "{{DataError}}" {{DOMException}}.
5857
+
1. If |condition| is not [=bidirectionally broadcastable=] to |descriptor|.{{MLOperandDescriptor/dimensions}}, then [=exception/throw=] a "{{DataError}}" {{DOMException}}.
5877
5858
1. If any of the following sub-steps fail, [=exception/throw=] an "{{OperationError}}" {{DOMException}}.
5878
5859
1. Let |output| be the result of [=creating an MLOperand=] given [=this=] and |descriptor|.
Broadcasting refers to how operations treat tensors with different shapes, and follow the precedent set by [[!numpy-broadcasting-rule]].
6076
+
6077
+
<div algorithm>
6078
+
To <dfn data-lt="unidirectionally broadcasting the shapes">unidirectionally broadcast the shapes</dfn> |A| and |B|, perform the following steps. |A| and |B| are [=/lists=] of positive integers, representing the dimensions of tensors, and the steps return a new [=/list=] of positive integers, or failure.
6079
+
6080
+
1. Let |sizeA| be the [=list/size=] of |A|.
6081
+
1. Let |sizeB| be the [=list/size=] of |B|.
6082
+
1. If |sizeB| > |sizeA|, then return failure.
6083
+
1. Let |paddedB| be a [=list/clone=] of |B|.
6084
+
1. While |paddedB|'s [=list/size=] is less than |sizeA|, [=list/prepend=] 1 to |paddedB|.
6085
+
1. Let |outputShape| be a new [=/list=].
6086
+
1. [=list/For each=] |index| in [=the range=] 0 to |sizeA|, exclusive:
6087
+
1. Let |dimA| be |A|[|index|].
6088
+
1. Let |dimB| be |paddedB|[|index|].
6089
+
1. If |dimA| is not equal to |dimB| and |dimA| is not equal to 1, then return failure.
6090
+
1. [=list/Append=] |dimA| to |outputShape|.
6091
+
1. Return |outputShape|.
6092
+
6093
+
</div>
6094
+
6095
+
<div algorithm>
6096
+
|A| is <dfn>unidirectionally broadcastable</dfn> to |B| if [=unidirectionally broadcasting the shapes=] |A| and |B| does not result in failure.
6097
+
</div>
6098
+
6099
+
<div algorithm>
6100
+
To <dfn data-lt="bidirectionally broadcasting the shapes">bidirectionally broadcast the shapes</dfn> |A| and |B|, perform the following steps. |A| and |B| are [=/lists=] of positive integers, representing the dimensions of tensors, and the steps return a new [=/list=] of positive integers, or failure.
6101
+
6102
+
1. Let |sizeA| be the [=list/size=] of |A|.
6103
+
1. Let |sizeB| be the [=list/size=] of |B|.
6104
+
1. Let |outputSize| be the maximum of |sizeA| and |sizeB|.
6105
+
1. Let |paddedA| be a [=list/clone=] of |A|.
6106
+
1. While |paddedA|'s [=list/size=] is less than |outputSize|, [=list/prepend=] 1 to |paddedA|.
6107
+
1. Let |paddedB| be a [=list/clone=] of |B|.
6108
+
1. While |paddedB|'s [=list/size=] is less than |outputSize|, [=list/prepend=] 1 to |paddedB|.
6109
+
1. Let |outputShape| be a new [=/list=].
6110
+
1. [=list/For each=] |index| in [=the range=] 0 to |outputSize|, exclusive:
6111
+
1. Let |dimA| be |paddedA|[|index|].
6112
+
1. Let |dimB| be |paddedB|[|index|].
6113
+
1. If |dimA| is not equal to |dimB|, and |dimA| is not equal to 1, and |dimB| is not equal to 1, then return failure.
6114
+
1. [=list/Append=] the maximum of |dimA| and |dimB| to |outputShape|.
6115
+
1. Return |outputShape|.
6116
+
6117
+
</div>
6118
+
6119
+
<div algorithm>
6120
+
|A| is <dfn>bidirectionally broadcastable</dfn> to |B| if [=bidirectionally broadcasting the shapes=] |A| and |B| does not result in failure.
0 commit comments