Skip to content

Commit 962ec76

Browse files
inexorabletashfdwr
andcommitted
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 parent e3f32fe commit 962ec76

File tree

1 file changed

+73
-39
lines changed

1 file changed

+73
-39
lines changed

index.bs

+73-39
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ Logo: https://webmachinelearning.github.io/webmachinelearning-logo.png
2323
Deadline: 2023-10-01
2424
Status Text: <p>
2525
Further implementation experience and user feedback is being gathered for the
26-
{{MLCommandEncoder}} interface that proposes to enable more efficient WebGPU
26+
<code>MLCommandEncoder</code> interface that proposes to enable more efficient WebGPU
2727
integration. A proposal to
2828
<a href="https://github.com/webmachinelearning/webnn/pull/322">simplify
2929
MLContext creation</a> is being discussed. This document is maintained and
@@ -2240,8 +2240,8 @@ partial interface MLGraphBuilder {
22402240
1. If |a|.{{MLOperand/[[descriptor]]}}.{{MLOperandDescriptor/dataType}} is not equal to |b|.{{MLOperand/[[descriptor]]}}.{{MLOperandDescriptor/dataType}}, then [=exception/throw=] a "{{DataError}}" {{DOMException}}.
22412241
1. Let |descriptor| be a new {{MLOperandDescriptor}}.
22422242
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}}.
22452245
1. If any of the following sub-steps fail, [=exception/throw=] an "{{OperationError}}" {{DOMException}}.
22462246
1. Let |output| be the result of [=creating an MLOperand=] given [=this=] and |descriptor|.
22472247
1. Make a request to the underlying platform to:
@@ -2255,21 +2255,6 @@ partial interface MLGraphBuilder {
22552255
</div>
22562256
</details>
22572257

2258-
<details open algorithm>
2259-
<summary>
2260-
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-
22732258
<details open>
22742259
<summary>
22752260
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
23762361
1. If |a|.{{MLOperand/[[descriptor]]}}.{{MLOperandDescriptor/dataType}} is not equal to |b|.{{MLOperand/[[descriptor]]}}.{{MLOperandDescriptor/dataType}}, then [=exception/throw=] a "{{DataError}}" {{DOMException}}.
23772362
1. Let |descriptor| be a new {{MLOperandDescriptor}}.
23782363
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}}.
23812366
1. If any of the following sub-steps fail, [=exception/throw=] an "{{OperationError}}" {{DOMException}}.
23822367
1. Let |output| be the result of [=creating an MLOperand=] given [=this=] and |descriptor|.
23832368
1. Make a request to the underlying platform to:
@@ -2710,16 +2695,12 @@ partial interface MLGraphBuilder {
27102695
<div class="note">
27112696
The permissions and context validity have been checked by [[#api-mlgraphbuilder-constructor]] steps.
27122697
</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}}.
27212702
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|.
27232704
1. Make a request to the underlying platform to:
27242705
1. Create [=platform operator=] |expandImpl| for this method, given |input| and |newShape|.
27252706
1. Set |output|.{{MLOperand/[[operator]]}} to |expandImpl|.
@@ -2862,7 +2843,7 @@ partial interface MLGraphBuilder {
28622843
</div>
28632844

28642845
### gemm ### {#api-mlgraphbuilder-gemm}
2865-
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.
28662847

28672848
<script type=idl>
28682849
dictionary MLGemmOptions {
@@ -2882,7 +2863,7 @@ partial interface MLGraphBuilder {
28822863
<dl dfn-type=dict-member dfn-for=MLGemmOptions>
28832864
: <dfn>c</dfn>
28842865
::
2885-
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.
28862867

28872868
: <dfn>alpha</dfn>
28882869
::
@@ -2922,7 +2903,7 @@ partial interface MLGraphBuilder {
29222903
1. If |options|.{{MLGemmOptions/aTranspose}} is true, then let |shapeA| be the reverse array of |shapeA|.
29232904
1. If |options|.{{MLGemmOptions/bTranspose}} is true, then let |shapeB| be the reverse array of |shapeB|.
29242905
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}}.
29262907
<div class="note">
29272908
Type compatibility between |a|, |b| and |options|.{{MLGemmOptions/c}} can be also checked.
29282909
</div>
@@ -3676,7 +3657,7 @@ partial interface MLGraphBuilder {
36763657
The <dfn method for=MLGraphBuilder>layerNormalization(|input|, |options|)</dfn> method steps are:
36773658
</summary>
36783659
<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.
36803661
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}}.
36813662
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}}.
36823663
1. [=list/For each=] |index| in [=the range=] 0 to the [=list/size=] of |options|.{{MLLayerNormalizationOptions/axes}}, exclusive:
@@ -4723,7 +4704,7 @@ partial interface MLGraphBuilder {
47234704
<div>
47244705
**Arguments:**
47254706
- *input*: an {{MLOperand}}. The input tensor.
4726-
- *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*.
47274708

47284709
**Returns:**
47294710
- an {{MLOperand}}. The output tensor of the same shape as *input*.
@@ -4737,8 +4718,8 @@ partial interface MLGraphBuilder {
47374718
<div class=algorithm-steps>
47384719
1. Let |descriptor| be a new {{MLOperandDescriptor}}.
47394720
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}}.
47424723
1. If any of the following sub-steps fail, [=exception/throw=] an "{{OperationError}}" {{DOMException}}.
47434724
1. Let |output| be the result of [=creating an MLOperand=] given [=this=] and |descriptor|.
47444725
1. Make a request to the underlying platform to:
@@ -5871,9 +5852,9 @@ partial interface MLGraphBuilder {
58715852
1. If |input|.{{MLOperand/[[descriptor]]}}.{{MLOperandDescriptor/dataType}} is not equal to |other|.{{MLOperand/[[descriptor]]}}.{{MLOperandDescriptor/dataType}}, then [=exception/throw=] a "{{DataError}}" {{DOMException}}.
58725853
1. Let |descriptor| be a new {{MLOperandDescriptor}}.
58735854
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}}.
58775858
1. If any of the following sub-steps fail, [=exception/throw=] an "{{OperationError}}" {{DOMException}}.
58785859
1. Let |output| be the result of [=creating an MLOperand=] given [=this=] and |descriptor|.
58795860
1. Make a request to the underlying platform to:
@@ -6086,6 +6067,59 @@ dictionary MLOperandDescriptor {
60866067
</div>
60876068
</details>
60886069

6070+
Algorithms {#algorithms}
6071+
=====================
6072+
6073+
## Broadcasting ## {#algorithms-broadcasting}
6074+
6075+
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.
6121+
</div>
6122+
60896123
Examples {#examples}
60906124
=====================
60916125

0 commit comments

Comments
 (0)