From a90a5cba08fdf6c0ceb95101c275108a152a3aed Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 12 Jun 2024 07:35:37 +0200 Subject: Merging upstream version 127.0. Signed-off-by: Daniel Baumann --- .../webnn/validation_tests/clamp.https.any.js | 53 +++ .../webnn/validation_tests/conv2d.https.any.js | 475 +++++++++++++++++++++ .../validation_tests/convTranspose2d.https.any.js | 470 ++++++++++++++++++++ .../tests/webnn/validation_tests/elu.https.any.js | 40 ++ .../webnn/validation_tests/expand.https.any.js | 63 +++ .../tests/webnn/validation_tests/gelu.https.any.js | 10 + .../tests/webnn/validation_tests/gemm.https.any.js | 140 ++++++ .../validation_tests/hardSigmoid.https.any.js | 28 ++ .../instanceNormalization.https.any.js | 149 +++++++ .../layerNormalization.https.any.js | 180 +++++++- .../webnn/validation_tests/leakyRelu.https.any.js | 28 ++ .../webnn/validation_tests/linear.https.any.js | 28 ++ .../webnn/validation_tests/matmul.https.any.js | 113 +++++ .../tests/webnn/validation_tests/pad.https.any.js | 70 +++ .../pooling-and-reduction-keep-dims.https.any.js | 94 ++++ .../webnn/validation_tests/reshape.https.any.js | 65 +++ .../webnn/validation_tests/slice.https.any.js | 66 +++ .../webnn/validation_tests/softplus.https.any.js | 3 + .../webnn/validation_tests/split.https.any.js | 80 ++++ .../webnn/validation_tests/transpose.https.any.js | 51 +++ 20 files changed, 2204 insertions(+), 2 deletions(-) create mode 100644 testing/web-platform/tests/webnn/validation_tests/gelu.https.any.js create mode 100644 testing/web-platform/tests/webnn/validation_tests/pooling-and-reduction-keep-dims.https.any.js (limited to 'testing/web-platform/tests/webnn/validation_tests') diff --git a/testing/web-platform/tests/webnn/validation_tests/clamp.https.any.js b/testing/web-platform/tests/webnn/validation_tests/clamp.https.any.js index 85cd19a566..126fa90e16 100644 --- a/testing/web-platform/tests/webnn/validation_tests/clamp.https.any.js +++ b/testing/web-platform/tests/webnn/validation_tests/clamp.https.any.js @@ -5,3 +5,56 @@ 'use strict'; validateInputFromAnotherBuilder('clamp'); + +validateUnaryOperation( + 'clamp', allWebNNOperandDataTypes, /*alsoBuildActivation=*/ true); + +promise_test(async t => { + const options = {minValue: 1.0, maxValue: 3.0}; + const input = + builder.input('input', {dataType: 'uint32', dimensions: [1, 2, 3]}); + const output = builder.clamp(input, options); + assert_equals(output.dataType(), 'uint32'); + assert_array_equals(output.shape(), [1, 2, 3]); +}, '[clamp] Test building an operator with options'); + +promise_test(async t => { + const options = {minValue: 0, maxValue: 0}; + const input = + builder.input('input', {dataType: 'int32', dimensions: [1, 2, 3, 4]}); + const output = builder.clamp(input, options); + assert_equals(output.dataType(), 'int32'); + assert_array_equals(output.shape(), [1, 2, 3, 4]); +}, '[clamp] Test building an operator with options.minValue == options.maxValue'); + +promise_test(async t => { + const options = {minValue: 2.0}; + builder.clamp(options); +}, '[clamp] Test building an activation with options'); + +promise_test(async t => { + const options = {minValue: 3.0, maxValue: 1.0}; + const input = + builder.input('input', {dataType: 'uint8', dimensions: [1, 2, 3]}); + assert_throws_js(TypeError, () => builder.clamp(input, options)); +}, '[clamp] Throw if options.minValue > options.maxValue when building an operator'); + +// To be removed once infinite `minValue` is allowed. Tracked in +// https://github.com/webmachinelearning/webnn/pull/647. +promise_test(async t => { + const options = {minValue: -Infinity}; + const input = builder.input('input', {dataType: 'float16', dimensions: []}); + assert_throws_js(TypeError, () => builder.clamp(input, options)); +}, '[clamp] Throw if options.minValue is -Infinity when building an operator'); + +promise_test(async t => { + const options = {minValue: 2.0, maxValue: -1.0}; + assert_throws_js(TypeError, () => builder.clamp(options)); +}, '[clamp] Throw if options.minValue > options.maxValue when building an activation'); + +// To be removed once NaN `maxValue` is allowed. Tracked in +// https://github.com/webmachinelearning/webnn/pull/647. +promise_test(async t => { + const options = {maxValue: NaN}; + assert_throws_js(TypeError, () => builder.clamp(options)); +}, '[clamp] Throw if options.maxValue is NaN when building an activation'); diff --git a/testing/web-platform/tests/webnn/validation_tests/conv2d.https.any.js b/testing/web-platform/tests/webnn/validation_tests/conv2d.https.any.js index ffc9c2c65d..7dac654951 100644 --- a/testing/web-platform/tests/webnn/validation_tests/conv2d.https.any.js +++ b/testing/web-platform/tests/webnn/validation_tests/conv2d.https.any.js @@ -55,3 +55,478 @@ multi_builder_test(async (t, builder, otherBuilder) => { const filter = builder.input('filter', kExampleFilterDescriptor); assert_throws_js(TypeError, () => builder.conv2d(input, filter, options)); }, '[conv2d] throw if activation option is from another builder'); + +const tests = [ + { + name: '[conv2d] Test with default options.', + input: {dataType: 'float32', dimensions: [1, 1, 5, 5]}, + filter: {dataType: 'float32', dimensions: [1, 1, 3, 3]}, + output: {dataType: 'float32', dimensions: [1, 1, 3, 3]} + }, + { + name: '[conv2d] Test with padding.', + input: {dataType: 'float32', dimensions: [1, 1, 5, 5]}, + filter: {dataType: 'float32', dimensions: [1, 1, 3, 3]}, + options: { + padding: [1, 1, 1, 1], + }, + output: {dataType: 'float32', dimensions: [1, 1, 5, 5]} + }, + { + name: '[conv2d] Test with strides and padding.', + input: {dataType: 'float32', dimensions: [1, 1, 5, 5]}, + filter: {dataType: 'float32', dimensions: [1, 1, 3, 3]}, + options: { + padding: [1, 1, 1, 1], + strides: [2, 2], + }, + output: {dataType: 'float32', dimensions: [1, 1, 3, 3]} + }, + { + name: '[conv2d] Test with strides and asymmetric padding.', + input: {dataType: 'float32', dimensions: [1, 1, 5, 5]}, + filter: {dataType: 'float32', dimensions: [1, 1, 4, 2]}, + options: { + padding: [1, 2, 0, 1], + strides: [2, 2], + }, + output: {dataType: 'float32', dimensions: [1, 1, 3, 3]} + }, + { + name: '[conv2d] Test depthwise conv2d by setting groups to input channels.', + input: {dataType: 'float32', dimensions: [1, 4, 2, 2]}, + filter: {dataType: 'float32', dimensions: [4, 1, 2, 2]}, + options: { + groups: 4, + }, + output: {dataType: 'float32', dimensions: [1, 4, 1, 1]} + }, + { + name: + '[conv2d] Test depthwise conv2d with groups, inputLayout="nhwc" and filterLayout="ihwo".', + input: {dataType: 'float32', dimensions: [1, 2, 2, 4]}, + filter: {dataType: 'float32', dimensions: [1, 2, 2, 4]}, + options: { + groups: 4, + inputLayout: 'nhwc', + filterLayout: 'ihwo', + }, + output: {dataType: 'float32', dimensions: [1, 1, 1, 4]} + }, + { + name: + '[conv2d] Test with dilations, inputLayout="nhwc" and filterLayout="ihwo".', + input: {dataType: 'float32', dimensions: [1, 65, 65, 1]}, + filter: {dataType: 'float32', dimensions: [1, 3, 3, 1]}, + options: { + inputLayout: 'nhwc', + filterLayout: 'ihwo', + dilations: [4, 4], + }, + output: {dataType: 'float32', dimensions: [1, 57, 57, 1]} + }, + { + name: '[conv2d] Test with inputLayout="nchw" and filterLayout="oihw".', + input: {dataType: 'float32', dimensions: [1, 2, 5, 5]}, + filter: {dataType: 'float32', dimensions: [1, 2, 3, 3]}, + options: { + inputLayout: 'nchw', + filterLayout: 'oihw', + }, + output: {dataType: 'float32', dimensions: [1, 1, 3, 3]} + }, + { + name: '[conv2d] Test with inputLayout="nchw" and filterLayout="hwio".', + input: {dataType: 'float32', dimensions: [1, 2, 5, 5]}, + filter: {dataType: 'float32', dimensions: [3, 3, 2, 1]}, + options: { + inputLayout: 'nchw', + filterLayout: 'hwio', + }, + output: {dataType: 'float32', dimensions: [1, 1, 3, 3]} + }, + { + name: '[conv2d] Test with inputLayout="nchw" and filterLayout="ohwi".', + input: {dataType: 'float32', dimensions: [1, 2, 5, 5]}, + filter: {dataType: 'float32', dimensions: [1, 3, 3, 2]}, + options: { + inputLayout: 'nchw', + filterLayout: 'ohwi', + }, + output: {dataType: 'float32', dimensions: [1, 1, 3, 3]} + }, + { + name: '[conv2d] Test with inputLayout="nchw" and filterLayout="ihwo".', + input: {dataType: 'float32', dimensions: [1, 2, 5, 5]}, + filter: {dataType: 'float32', dimensions: [2, 3, 3, 1]}, + options: { + inputLayout: 'nchw', + filterLayout: 'ihwo', + }, + output: {dataType: 'float32', dimensions: [1, 1, 3, 3]} + }, + { + name: '[conv2d] Test with inputLayout="nhwc" and filterLayout="oihw".', + input: {dataType: 'float32', dimensions: [1, 5, 5, 2]}, + filter: {dataType: 'float32', dimensions: [1, 2, 3, 3]}, + options: { + inputLayout: 'nhwc', + filterLayout: 'oihw', + }, + output: {dataType: 'float32', dimensions: [1, 3, 3, 1]} + }, + { + name: '[conv2d] Test with inputLayout="nhwc" and filterLayout="hwio".', + input: {dataType: 'float32', dimensions: [1, 5, 5, 2]}, + filter: {dataType: 'float32', dimensions: [3, 3, 2, 1]}, + options: { + inputLayout: 'nhwc', + filterLayout: 'hwio', + }, + output: {dataType: 'float32', dimensions: [1, 3, 3, 1]} + }, + { + name: '[conv2d] Test with inputLayout="nhwc" and filterLayout="ohwi".', + input: {dataType: 'float32', dimensions: [1, 5, 5, 2]}, + filter: {dataType: 'float32', dimensions: [1, 3, 3, 2]}, + options: { + inputLayout: 'nhwc', + filterLayout: 'ohwi', + }, + output: {dataType: 'float32', dimensions: [1, 3, 3, 1]} + }, + { + name: '[conv2d] Test with inputLayout="nhwc" and filterLayout="ihwo".', + input: {dataType: 'float32', dimensions: [1, 5, 5, 2]}, + filter: {dataType: 'float32', dimensions: [2, 3, 3, 1]}, + options: { + inputLayout: 'nhwc', + filterLayout: 'ihwo', + }, + output: {dataType: 'float32', dimensions: [1, 3, 3, 1]} + }, + { + name: '[conv2d] Throw if the input is not a 4-D tensor.', + input: {dataType: 'float32', dimensions: [1, 5, 5]}, + filter: {dataType: 'float32', dimensions: [1, 2, 2, 1]}, + }, + { + name: '[conv2d] Throw if the input data type is not floating point.', + input: {dataType: 'int32', dimensions: [1, 1, 5, 5]}, + filter: {dataType: 'int32', dimensions: [1, 1, 2, 2]}, + }, + { + name: '[conv2d] Throw if the filter is not a 4-D tensor.', + input: {dataType: 'float32', dimensions: [1, 1, 5, 5]}, + filter: {dataType: 'float32', dimensions: [2, 2]}, + }, + { + name: + '[conv2d] Throw if the filter data type doesn\'t match the input data type.', + input: {dataType: 'float32', dimensions: [1, 1, 5, 5]}, + filter: {dataType: 'int32', dimensions: [1, 1, 2, 2]}, + }, + { + name: '[conv2d] Throw if the length of padding is not 4.', + input: {dataType: 'float32', dimensions: [1, 1, 5, 5]}, + filter: {dataType: 'float32', dimensions: [1, 1, 2, 2]}, + options: { + padding: [2, 2], + }, + }, + { + name: '[conv2d] Throw if the length of strides is not 2.', + input: {dataType: 'float32', dimensions: [1, 1, 5, 5]}, + filter: {dataType: 'float32', dimensions: [1, 1, 2, 2]}, + options: { + strides: [2], + }, + }, + { + name: '[conv2d] Throw if strideHeight is smaller than 1.', + input: {dataType: 'float32', dimensions: [1, 1, 5, 5]}, + filter: {dataType: 'float32', dimensions: [1, 1, 2, 2]}, + options: { + strides: [0, 1], + }, + }, + { + name: '[conv2d] Throw if strideWidth is smaller than 1.', + input: {dataType: 'float32', dimensions: [1, 1, 5, 5]}, + filter: {dataType: 'float32', dimensions: [1, 1, 2, 2]}, + options: { + strides: [1, 0], + }, + }, + { + name: '[conv2d] Throw if the length of dilations is not 2.', + input: {dataType: 'float32', dimensions: [1, 1, 5, 5]}, + filter: {dataType: 'float32', dimensions: [1, 1, 2, 2]}, + options: { + dilations: [1], + }, + }, + { + name: '[conv2d] Throw if dilationHeight is smaller than 1.', + input: {dataType: 'float32', dimensions: [1, 1, 5, 5]}, + filter: {dataType: 'float32', dimensions: [1, 1, 2, 2]}, + options: { + dilations: [0, 1], + }, + }, + { + name: '[conv2d] Throw if dilationWidth is smaller than 1.', + input: {dataType: 'float32', dimensions: [1, 1, 5, 5]}, + filter: {dataType: 'float32', dimensions: [1, 1, 2, 2]}, + options: { + dilations: [1, 0], + }, + }, + { + name: '[conv2d] Throw if inputChannels % groups is not 0.', + input: {dataType: 'float32', dimensions: [1, 4, 5, 5]}, + filter: {dataType: 'float32', dimensions: [1, 1, 2, 2]}, + options: { + groups: 3, + }, + }, + { + name: + '[conv2d] Throw if inputChannels / groups is not equal to filterInputChannels.', + input: {dataType: 'float32', dimensions: [1, 4, 5, 5]}, + filter: {dataType: 'float32', dimensions: [1, 1, 2, 2]}, + options: { + groups: 2, + }, + }, + { + name: '[conv2d] Throw if the groups is smaller than 1.', + input: {dataType: 'float32', dimensions: [1, 4, 5, 5]}, + filter: {dataType: 'float32', dimensions: [1, 1, 2, 2]}, + options: { + groups: 0, + }, + }, + { + name: + '[conv2d] Throw due to overflow when calculating the effective filter height.', + input: {dataType: 'float32', dimensions: [1, 1, 5, 5]}, + filter: {dataType: 'float32', dimensions: [1, 1, 434983, 2]}, + options: { + dilations: [328442, 1], + }, + }, + { + name: + '[conv2d] Throw due to overflow when calculating the effective filter width.', + input: {dataType: 'float32', dimensions: [1, 1, 5, 5]}, + filter: {dataType: 'float32', dimensions: [1, 1, 2, 234545]}, + options: { + dilations: [2, 843452], + }, + }, + { + name: '[conv2d] Throw due to overflow when dilation height is too large.', + input: {dataType: 'float32', dimensions: [1, 1, 5, 5]}, + filter: {dataType: 'float32', dimensions: [1, 1, 3, 3]}, + options: { + dilations: [kMaxUnsignedLong, 1], + }, + }, + { + name: '[conv2d] Throw due to overflow when dilation width is too large.', + input: {dataType: 'float32', dimensions: [1, 1, 5, 5]}, + filter: {dataType: 'float32', dimensions: [1, 1, 3, 3]}, + options: { + dilations: [1, kMaxUnsignedLong], + }, + }, + { + name: '[conv2d] Throw due to underflow when calculating the output height.', + input: {dataType: 'float32', dimensions: [1, 1, 5, 5]}, + filter: {dataType: 'float32', dimensions: [1, 1, 4, 2]}, + options: { + dilations: [4, 1], + padding: [1, 1, 1, 1], + strides: [2, 2], + }, + }, + { + name: '[conv2d] Throw due to underflow when calculating the output width.', + input: {dataType: 'float32', dimensions: [1, 1, 5, 5]}, + filter: {dataType: 'float32', dimensions: [1, 1, 2, 8]}, + options: { + dilations: [1, 4], + padding: [1, 1, 1, 1], + strides: [2, 2], + }, + }, + { + name: '[conv2d] Throw if the bias is not a 1-D tensor.', + input: {dataType: 'float32', dimensions: [1, 1, 5, 5]}, + filter: {dataType: 'float32', dimensions: [1, 1, 2, 2]}, + options: { + bias: {dataType: 'float32', dimensions: [1, 2]}, + }, + }, + { + name: + '[conv2d] Throw if the bias shape is not equal to [output_channels] with filterLayout="oihw".', + input: {dataType: 'float32', dimensions: [1, 1, 5, 5]}, + filter: {dataType: 'float32', dimensions: [1, 1, 2, 2]}, + options: { + bias: {dataType: 'float32', dimensions: [2]}, + }, + }, + { + name: + '[conv2d] Throw if the bias shape is not equal to [output_channels] with filterLayout="hwio".', + input: {dataType: 'float32', dimensions: [1, 1, 5, 5]}, + filter: {dataType: 'float32', dimensions: [2, 2, 1, 1]}, + options: { + bias: {dataType: 'float32', dimensions: [2]}, + }, + }, + { + name: + '[conv2d] Throw if the bias shape is not equal to [output_channels] with filterLayout="ohwi".', + input: {dataType: 'float32', dimensions: [1, 1, 5, 5]}, + filter: {dataType: 'float32', dimensions: [1, 2, 2, 1]}, + options: { + bias: {dataType: 'float32', dimensions: [2]}, + }, + }, + { + name: + '[conv2d] Throw if the bias shape is not equal to [output_channels] with filterLayout="ihwo".', + input: {dataType: 'float32', dimensions: [1, 1, 5, 5]}, + filter: {dataType: 'float32', dimensions: [1, 2, 2, 1]}, + options: { + bias: {dataType: 'float32', dimensions: [2]}, + }, + }, + { + name: + '[conv2d] Throw if the bias data type doesn\'t match input data type.', + input: {dataType: 'float32', dimensions: [1, 1, 5, 5]}, + filter: {dataType: 'float32', dimensions: [1, 1, 2, 2]}, + options: { + bias: {dataType: 'int32', dimensions: [1]}, + }, + }, + { + name: + '[conv2d] Throw if inputChannels / groups is not equal to filterInputChannels with inputLayout="nchw" and filterLayout="oihw".', + input: {dataType: 'float32', dimensions: [1, 2, 5, 5]}, + filter: {dataType: 'float32', dimensions: [1, 2, 3, 3]}, + options: { + inputLayout: 'nchw', + filterLayout: 'oihw', + groups: 2, + }, + }, + { + name: + '[conv2d] Throw if inputChannels / groups is not equal to filterInputChannels with inputLayout="nchw" and filterLayout="hwio".', + input: {dataType: 'float32', dimensions: [1, 2, 5, 5]}, + filter: {dataType: 'float32', dimensions: [3, 3, 2, 1]}, + options: { + inputLayout: 'nchw', + filterLayout: 'hwio', + groups: 2, + }, + }, + { + name: + '[conv2d] Throw if inputChannels / groups is not equal to filterInputChannels with inputLayout="nchw" and filterLayout="ohwi".', + input: {dataType: 'float32', dimensions: [1, 2, 5, 5]}, + filter: {dataType: 'float32', dimensions: [1, 3, 3, 2]}, + options: { + inputLayout: 'nchw', + filterLayout: 'ohwi', + groups: 2, + }, + }, + { + name: + '[conv2d] Throw if inputChannels / groups is not equal to filterInputChannels with inputLayout="nchw" and filterLayout="ihwo".', + input: {dataType: 'float32', dimensions: [1, 2, 5, 5]}, + filter: {dataType: 'float32', dimensions: [2, 3, 3, 1]}, + options: { + inputLayout: 'nchw', + filterLayout: 'ihwo', + groups: 2, + }, + + }, + { + name: + '[conv2d] Throw if inputChannels / groups is not equal to filterInputChannels with inputLayout="nhwc" and filterLayout="oihw".', + input: {dataType: 'float32', dimensions: [1, 5, 5, 2]}, + filter: {dataType: 'float32', dimensions: [1, 2, 3, 3]}, + options: { + inputLayout: 'nhwc', + filterLayout: 'oihw', + groups: 2, + }, + }, + { + name: + '[conv2d] Throw if inputChannels / groups is not equal to filterInputChannels with inputLayout="nhwc" and filterLayout="hwio".', + input: {dataType: 'float32', dimensions: [1, 5, 5, 2]}, + filter: {dataType: 'float32', dimensions: [3, 3, 2, 1]}, + options: { + inputLayout: 'nhwc', + filterLayout: 'hwio', + groups: 2, + }, + }, + { + name: + '[conv2d] Throw if inputChannels / groups is not equal to filterInputChannels with inputLayout="nhwc" and filterLayout="ohwi".', + input: {dataType: 'float32', dimensions: [1, 5, 5, 2]}, + filter: {dataType: 'float32', dimensions: [1, 3, 3, 2]}, + options: { + inputLayout: 'nhwc', + filterLayout: 'ohwi', + groups: 2, + }, + }, + { + name: + '[conv2d] Throw if inputChannels / groups is not equal to filterInputChannels with inputLayout="nhwc" and filterLayout="ihwo".', + input: {dataType: 'float32', dimensions: [1, 5, 5, 2]}, + filter: {dataType: 'float32', dimensions: [2, 3, 3, 1]}, + options: { + inputLayout: 'nhwc', + filterLayout: 'ihwo', + groups: 2, + }, + }, +]; + +tests.forEach( + test => promise_test(async t => { + const input = builder.input( + 'input', + {dataType: test.input.dataType, dimensions: test.input.dimensions}); + const filter = builder.input( + 'filter', + {dataType: test.filter.dataType, dimensions: test.filter.dimensions}); + + if (test.options && test.options.bias) { + test.options.bias = builder.input('bias', { + dataType: test.options.bias.dataType, + dimensions: test.options.bias.dimensions + }); + } + + if (test.output) { + const output = builder.conv2d(input, filter, test.options); + assert_equals(output.dataType(), test.output.dataType); + assert_array_equals(output.shape(), test.output.dimensions); + } else { + assert_throws_js( + TypeError, () => builder.conv2d(input, filter, test.options)); + } + }, test.name)); diff --git a/testing/web-platform/tests/webnn/validation_tests/convTranspose2d.https.any.js b/testing/web-platform/tests/webnn/validation_tests/convTranspose2d.https.any.js index c14f445bf3..02822c5274 100644 --- a/testing/web-platform/tests/webnn/validation_tests/convTranspose2d.https.any.js +++ b/testing/web-platform/tests/webnn/validation_tests/convTranspose2d.https.any.js @@ -57,3 +57,473 @@ multi_builder_test(async (t, builder, otherBuilder) => { assert_throws_js( TypeError, () => builder.convTranspose2d(input, filter, options)); }, '[convTranspose2d] throw if activation option is from another builder'); + +const tests = [ + { + name: '[convTranspose2d] Test with default options.', + input: {dataType: 'float32', dimensions: [1, 1, 3, 3]}, + filter: {dataType: 'float32', dimensions: [1, 1, 3, 3]}, + output: {dataType: 'float32', dimensions: [1, 1, 5, 5]} + }, + { + name: + '[convTranspose2d] Test with inputLayout="nchw" and filterLayout="hwoi".', + input: {dataType: 'float32', dimensions: [1, 1, 3, 3]}, + filter: {dataType: 'float32', dimensions: [3, 3, 2, 1]}, + options: { + filterLayout: 'hwoi', + inputLayout: 'nchw', + }, + output: {dataType: 'float32', dimensions: [1, 2, 5, 5]} + }, + { + name: + '[convTranspose2d] Test with inputLayout="nchw" and filterLayout="ohwi".', + input: {dataType: 'float32', dimensions: [1, 1, 3, 3]}, + filter: {dataType: 'float32', dimensions: [2, 3, 3, 1]}, + options: { + filterLayout: 'ohwi', + inputLayout: 'nchw', + }, + output: {dataType: 'float32', dimensions: [1, 2, 5, 5]} + }, + { + name: + '[convTranspose2d] Test with inputLayout="nhwc" and filterLayout="iohw".', + input: {dataType: 'float32', dimensions: [1, 3, 3, 1]}, + filter: {dataType: 'float32', dimensions: [1, 2, 3, 3]}, + options: { + filterLayout: 'iohw', + inputLayout: 'nhwc', + }, + output: {dataType: 'float32', dimensions: [1, 5, 5, 2]} + }, + { + name: + '[convTranspose2d] Test with inputLayout="nhwc" and filterLayout="hwoi".', + input: {dataType: 'float32', dimensions: [1, 3, 3, 1]}, + filter: {dataType: 'float32', dimensions: [3, 3, 2, 1]}, + options: { + filterLayout: 'hwoi', + inputLayout: 'nhwc', + }, + output: {dataType: 'float32', dimensions: [1, 5, 5, 2]} + }, + { + name: + '[convTranspose2d] Test with inputLayout="nhwc" and filterLayout="ohwi".', + input: {dataType: 'float32', dimensions: [1, 3, 3, 1]}, + filter: {dataType: 'float32', dimensions: [2, 3, 3, 1]}, + options: { + filterLayout: 'ohwi', + inputLayout: 'nhwc', + }, + output: {dataType: 'float32', dimensions: [1, 5, 5, 2]} + }, + { + name: '[convTranspose2d] Test with strides=[3, 2], outputSizes=[10, 8].', + input: {dataType: 'float32', dimensions: [1, 1, 3, 3]}, + filter: {dataType: 'float32', dimensions: [1, 2, 3, 3]}, + options: { + strides: [3, 2], + outputSizes: [10, 8], + }, + output: {dataType: 'float32', dimensions: [1, 2, 10, 8]} + }, + { + name: '[convTranspose2d] Test with strides=[3, 2], outputPadding=[1, 1].', + input: {dataType: 'float32', dimensions: [1, 1, 3, 3]}, + filter: {dataType: 'float32', dimensions: [1, 2, 3, 3]}, + options: { + strides: [3, 2], + outputPadding: [1, 1], + }, + output: {dataType: 'float32', dimensions: [1, 2, 10, 8]} + }, + { + name: '[convTranspose2d] Test with padding=1.', + input: {dataType: 'float32', dimensions: [1, 1, 5, 5]}, + filter: {dataType: 'float32', dimensions: [1, 1, 3, 3]}, + options: { + padding: [1, 1, 1, 1], + }, + output: {dataType: 'float32', dimensions: [1, 1, 5, 5]} + }, + { + name: '[convTranspose2d] Test with padding=1, groups=3.', + input: {dataType: 'float32', dimensions: [1, 1, 5, 5]}, + filter: {dataType: 'float32', dimensions: [1, 1, 3, 3]}, + options: { + padding: [1, 1, 1, 1], + groups: 3, + }, + output: {dataType: 'float32', dimensions: [1, 3, 5, 5]} + }, + { + name: '[convTranspose2d] Test with strides=2.', + input: {dataType: 'float32', dimensions: [1, 1, 3, 3]}, + filter: {dataType: 'float32', dimensions: [1, 2, 3, 3]}, + options: { + strides: [2, 2], + }, + output: {dataType: 'float32', dimensions: [1, 2, 7, 7]} + }, + { + name: '[convTranspose2d] Test with strides=2 and padding=1.', + input: {dataType: 'float32', dimensions: [1, 1, 3, 3]}, + filter: {dataType: 'float32', dimensions: [1, 1, 3, 3]}, + options: { + padding: [1, 1, 1, 1], + strides: [2, 2], + }, + output: {dataType: 'float32', dimensions: [1, 1, 5, 5]} + }, + { + name: + '[convTranspose2d] Test when the output sizes are explicitly specified, the output padding values are ignored though padding value is not smaller than stride along the same axis.', + input: {dataType: 'float32', dimensions: [1, 1, 3, 3]}, + filter: {dataType: 'float32', dimensions: [1, 2, 3, 3]}, + options: { + outputPadding: [3, 3], + strides: [3, 2], + outputSizes: [10, 8], + }, + output: {dataType: 'float32', dimensions: [1, 2, 10, 8]} + }, + { + name: '[convTranspose2d] Throw if the input is not a 4-D tensor.', + input: {dataType: 'float32', dimensions: [1, 5, 5]}, + filter: {dataType: 'float32', dimensions: [1, 1, 2, 2]}, + }, + { + name: + '[convTranspose2d] Throw if the input data type is not floating point.', + input: {dataType: 'int32', dimensions: [1, 1, 5, 5]}, + filter: {dataType: 'int32', dimensions: [1, 1, 2, 2]}, + }, + { + name: '[convTranspose2d] Throw if the filter is not a 4-D tensor.', + input: {dataType: 'float32', dimensions: [1, 1, 5, 5]}, + filter: {dataType: 'float32', dimensions: [2, 2]}, + }, + { + name: + '[convTranspose2d] Throw if the filter data type doesn\'t match the input data type.', + input: {dataType: 'float32', dimensions: [1, 1, 5, 5]}, + filter: {dataType: 'int32', dimensions: [1, 1, 2, 2]}, + }, + { + name: '[convTranspose2d] Throw if the length of padding is not 4.', + input: {dataType: 'float32', dimensions: [1, 1, 5, 5]}, + filter: {dataType: 'float32', dimensions: [1, 1, 2, 2]}, + options: { + padding: [2, 2], + }, + }, + { + name: '[convTranspose2d] Throw if the length of strides is not 2.', + input: {dataType: 'float32', dimensions: [1, 1, 5, 5]}, + filter: {dataType: 'float32', dimensions: [1, 1, 2, 2]}, + options: { + strides: [2], + }, + }, + { + name: '[convTranspose2d] Throw if one stride value is smaller than 1.', + input: {dataType: 'float32', dimensions: [1, 1, 5, 5]}, + filter: {dataType: 'float32', dimensions: [1, 1, 2, 2]}, + options: { + strides: [1, 0], + }, + }, + { + name: '[convTranspose2d] Throw if the length of dilations is not 2.', + input: {dataType: 'float32', dimensions: [1, 1, 5, 5]}, + filter: {dataType: 'float32', dimensions: [1, 1, 2, 2]}, + options: { + dilations: [1], + }, + }, + { + name: + '[convTranspose2d] Throw if the one dilation value is smaller than 1.', + input: {dataType: 'float32', dimensions: [1, 1, 5, 5]}, + filter: {dataType: 'float32', dimensions: [1, 1, 2, 2]}, + options: { + dilations: [1, 0], + }, + }, + { + name: + '[convTranspose2d] Throw if the input channels is not equal to the filter input channels with inputLayout="nchw" and filterLayout="iohw".', + input: {dataType: 'float32', dimensions: [1, 3, 3, 3]}, + filter: {dataType: 'float32', dimensions: [1, 1, 3, 3]}, + options: { + filterLayout: 'iohw', + inputLayout: 'nchw', + groups: 1, + }, + }, + { + name: + '[convTranspose2d] Throw if the input channels is not equal to the filter input channels with inputLayout="nchw" and filterLayout="hwoi".', + input: {dataType: 'float32', dimensions: [1, 3, 3, 3]}, + filter: {dataType: 'float32', dimensions: [3, 1, 2, 1]}, + options: { + filterLayout: 'hwoi', + inputLayout: 'nchw', + }, + }, + { + name: + '[convTranspose2d] Throw if the input channels is not equal to the filter input channels with inputLayout="nchw" and filterLayout="ohwi".', + input: {dataType: 'float32', dimensions: [1, 2, 3, 3]}, + filter: {dataType: 'float32', dimensions: [2, 3, 3, 1]}, + options: { + filterLayout: 'ohwi', + inputLayout: 'nchw', + }, + }, + { + name: + '[convTranspose2d] Throw if the input channels is not equal to the filter input channels with inputLayout="nhwc" and filterLayout="iohw".', + input: {dataType: 'float32', dimensions: [1, 3, 3, 2]}, + filter: {dataType: 'float32', dimensions: [1, 2, 3, 3]}, + options: { + filterLayout: 'iohw', + inputLayout: 'nhwc', + }, + }, + { + name: + '[convTranspose2d] Throw if the input channels is not equal to the filter input channels inputLayout="nhwc" and filterLayout="hwoi".', + input: {dataType: 'float32', dimensions: [1, 3, 3, 2]}, + filter: {dataType: 'float32', dimensions: [3, 3, 2, 1]}, + options: { + filterLayout: 'hwoi', + inputLayout: 'nhwc', + }, + }, + { + name: + '[convTranspose2d] Throw if the input channels is not equal to the filter input channels with inputLayout="nhwc" and filterLayout="ohwi".', + input: {dataType: 'float32', dimensions: [1, 3, 3, 2]}, + filter: {dataType: 'float32', dimensions: [2, 3, 3, 1]}, + options: { + filterLayout: 'ohwi', + inputLayout: 'nhwc', + }, + }, + { + name: '[convTranspose2d] Throw if output channels is too large.', + input: {dataType: 'float32', dimensions: [1, 4, 5, 5]}, + filter: {dataType: 'float32', dimensions: [4, 2, 2, 2]}, + options: { + groups: kMaxUnsignedLong, + }, + }, + { + name: '[convTranspose2d] Throw if the groups is smaller than 1.', + input: {dataType: 'float32', dimensions: [1, 4, 5, 5]}, + filter: {dataType: 'float32', dimensions: [1, 1, 2, 2]}, + options: { + groups: 0, + }, + }, + { + name: + '[convTranspose2d] Throw due to overflow when calculating the effective filter height.', + input: {dataType: 'float32', dimensions: [1, 1, 5, 5]}, + filter: {dataType: 'float32', dimensions: [1, 1, 434983, 2]}, + options: { + dilations: [328443, 1], + }, + }, + { + name: + '[convTranspose2d] Throw due to overflow when calculating the effective filter width.', + input: {dataType: 'float32', dimensions: [1, 1, 5, 5]}, + filter: {dataType: 'float32', dimensions: [1, 1, 2, 234545]}, + options: { + dilations: [2, 843452], + }, + }, + { + name: + '[convTranspose2d] Throw due to overflow when dilation height is too large.', + input: {dataType: 'float32', dimensions: [1, 1, 5, 5]}, + filter: {dataType: 'float32', dimensions: [1, 1, 3, 2]}, + options: { + dilations: [kMaxUnsignedLong, 1], + }, + }, + { + name: + '[convTranspose2d] Throw due to overflow when dilation width is too large.', + input: {dataType: 'float32', dimensions: [1, 1, 5, 5]}, + filter: {dataType: 'float32', dimensions: [1, 1, 3, 2]}, + options: { + dilations: [1, kMaxUnsignedLong], + }, + }, + { + name: '[convTranspose2d] Throw if the bias is not a 1-D tensor.', + input: {dataType: 'float32', dimensions: [1, 1, 5, 5]}, + filter: {dataType: 'float32', dimensions: [1, 1, 2, 2]}, + options: { + bias: {dataType: 'float32', dimensions: [1, 2]}, + }, + }, + { + name: + '[convTranspose2d] Throw if the bias shape is not equal to [output_channels] with filterLayout="iohw".', + input: {dataType: 'float32', dimensions: [1, 1, 5, 5]}, + filter: {dataType: 'float32', dimensions: [1, 1, 2, 2]}, + options: { + filterLayout: 'iohw', + bias: {dataType: 'float32', dimensions: [2]}, + }, + }, + { + name: + '[convTranspose2d] Throw if the bias shape is not equal to [output_channels] with filterLayout="hwoi".', + input: {dataType: 'float32', dimensions: [1, 1, 5, 5]}, + filter: {dataType: 'float32', dimensions: [2, 2, 1, 1]}, + options: { + filterLayout: 'hwoi', + bias: {dataType: 'float32', dimensions: [2]}, + }, + }, + { + name: + '[convTranspose2d] Throw if the bias shape is not equal to [output_channels] with filterLayout="ohwi".', + input: {dataType: 'float32', dimensions: [1, 1, 5, 5]}, + filter: {dataType: 'float32', dimensions: [1, 2, 2, 1]}, + options: { + filterLayout: 'ohwi', + bias: {dataType: 'float32', dimensions: [2]}, + }, + }, + { + name: + '[convTranspose2d] Throw if the bias data type doesn\'t match input data type.', + input: {dataType: 'float32', dimensions: [1, 1, 5, 5]}, + filter: {dataType: 'float32', dimensions: [1, 1, 2, 2]}, + options: { + bias: {dataType: 'int32', dimensions: [1]}, + }, + }, + { + name: + '[convTranspose2d] Throw if the outputPadding is not a sequence of length 2.', + input: {dataType: 'float32', dimensions: [1, 1, 3, 3]}, + filter: {dataType: 'float32', dimensions: [1, 2, 3, 3]}, + options: { + strides: [3, 2], + outputPadding: [1, 1, 1, 1], + }, + }, + { + name: + '[convTranspose2d] Throw if the outputPadding is not smaller than stride along the width dimension.', + input: {dataType: 'float32', dimensions: [1, 1, 2, 2]}, + filter: {dataType: 'float32', dimensions: [1, 1, 3, 3]}, + options: { + padding: [0, 0, 3, 3], + strides: [2, 2], + outputPadding: [0, 2], + }, + }, + { + name: + '[convTranspose2d] Throw if the outputPadding is not smaller than stride along the height dimension.', + input: {dataType: 'float32', dimensions: [1, 1, 2, 2]}, + filter: {dataType: 'float32', dimensions: [1, 1, 3, 3]}, + options: { + padding: [0, 0, 3, 3], + strides: [2, 2], + outputPadding: [2, 0], + }, + }, + { + name: + '[convTranspose2d] Throw if the outputSizes is not a sequence of length 2.', + input: {dataType: 'float32', dimensions: [1, 1, 3, 3]}, + filter: {dataType: 'float32', dimensions: [1, 2, 3, 3]}, + options: { + strides: [3, 2], + outputSizes: [1, 2, 10, 8], + }, + }, + { + name: '[convTranspose2d] Throw if the padding height is too large.', + input: {dataType: 'float32', dimensions: [1, 1, 2, 2]}, + filter: {dataType: 'float32', dimensions: [1, 1, 3, 3]}, + options: { + padding: [4, 4, 0, 0], + strides: [2, 2], + outputPadding: [1, 0], + }, + }, + { + name: '[convTranspose2d] Throw if the padding width is too large.', + input: {dataType: 'float32', dimensions: [1, 1, 2, 2]}, + filter: {dataType: 'float32', dimensions: [1, 1, 3, 3]}, + options: { + padding: [0, 0, 4, 4], + strides: [2, 2], + outputPadding: [0, 1], + }, + }, + { + name: + '[convTranspose2d] Throw due to outputSizes values are smaller than the output sizes calculated by not using outputPadding.', + input: {dataType: 'float32', dimensions: [1, 1, 3, 3]}, + filter: {dataType: 'float32', dimensions: [1, 1, 3, 3]}, + options: { + padding: [1, 1, 1, 1], + strides: [2, 2], + outputSizes: [4, 4], + outputPadding: [1, 1], + }, + }, + { + name: + '[convTranspose2d] Throw due to outputSizes values are greater than the output sizes calculated by not using outputPadding.', + input: {dataType: 'float32', dimensions: [1, 1, 3, 3]}, + filter: {dataType: 'float32', dimensions: [1, 1, 3, 3]}, + options: { + padding: [1, 1, 1, 1], + strides: [2, 2], + outputSizes: [6, 8], + outputPadding: [1, 1], + }, + }, +]; + +tests.forEach( + test => promise_test(async t => { + const input = builder.input( + 'input', + {dataType: test.input.dataType, dimensions: test.input.dimensions}); + const filter = builder.input( + 'filter', + {dataType: test.filter.dataType, dimensions: test.filter.dimensions}); + + if (test.options && test.options.bias) { + test.options.bias = builder.input('bias', { + dataType: test.options.bias.dataType, + dimensions: test.options.bias.dimensions + }); + } + + if (test.output) { + const output = builder.convTranspose2d(input, filter, test.options); + assert_equals(output.dataType(), test.output.dataType); + assert_array_equals(output.shape(), test.output.dimensions); + } else { + assert_throws_js( + TypeError, + () => builder.convTranspose2d(input, filter, test.options)); + } + }, test.name)); diff --git a/testing/web-platform/tests/webnn/validation_tests/elu.https.any.js b/testing/web-platform/tests/webnn/validation_tests/elu.https.any.js index 6e842cb691..53ec5e54ae 100644 --- a/testing/web-platform/tests/webnn/validation_tests/elu.https.any.js +++ b/testing/web-platform/tests/webnn/validation_tests/elu.https.any.js @@ -5,3 +5,43 @@ 'use strict'; validateInputFromAnotherBuilder('elu'); + +validateUnaryOperation( + 'elu', floatingPointTypes, /*alsoBuildActivation=*/ true); + +promise_test(async t => { + const options = {alpha: 1.0}; + const input = + builder.input('input', {dataType: 'float32', dimensions: [1, 2, 3]}); + const output = builder.elu(input, options); + assert_equals(output.dataType(), 'float32'); + assert_array_equals(output.shape(), [1, 2, 3]); +}, '[elu] Test building an operator with options'); + +promise_test(async t => { + const options = {alpha: 1.5}; + builder.elu(options); +}, '[elu] Test building an activation with options'); + +promise_test(async t => { + const options = {alpha: -1.0}; + const input = + builder.input('input', {dataType: 'float32', dimensions: [1, 2, 3]}); + assert_throws_js(TypeError, () => builder.elu(input, options)); +}, '[elu] Throw if options.alpha <= 0 when building an operator'); + +promise_test(async t => { + const options = {alpha: NaN}; + const input = builder.input('input', {dataType: 'float16', dimensions: []}); + assert_throws_js(TypeError, () => builder.elu(input, options)); +}, '[elu] Throw if options.alpha is NaN when building an operator'); + +promise_test(async t => { + const options = {alpha: 0}; + assert_throws_js(TypeError, () => builder.elu(options)); +}, '[elu] Throw if options.alpha <= 0 when building an activation'); + +promise_test(async t => { + const options = {alpha: Infinity}; + assert_throws_js(TypeError, () => builder.elu(options)); +}, '[elu] Throw if options.alpha is Infinity when building an activation'); diff --git a/testing/web-platform/tests/webnn/validation_tests/expand.https.any.js b/testing/web-platform/tests/webnn/validation_tests/expand.https.any.js index d90ab89468..088d826df7 100644 --- a/testing/web-platform/tests/webnn/validation_tests/expand.https.any.js +++ b/testing/web-platform/tests/webnn/validation_tests/expand.https.any.js @@ -12,3 +12,66 @@ multi_builder_test(async (t, builder, otherBuilder) => { assert_throws_js( TypeError, () => builder.expand(inputFromOtherBuilder, newShape)); }, '[expand] throw if input is from another builder'); + +const tests = [ + { + name: '[expand] Test with 0-D scalar to 3-D tensor.', + input: {dataType: 'float32', dimensions: []}, + newShape: [3, 4, 5], + output: {dataType: 'float32', dimensions: [3, 4, 5]} + }, + { + name: '[expand] Test with the new shapes that are the same as input.', + input: {dataType: 'float32', dimensions: [4]}, + newShape: [4], + output: {dataType: 'float32', dimensions: [4]} + }, + { + name: '[expand] Test with the new shapes that are broadcastable.', + input: {dataType: 'int32', dimensions: [3, 1, 5]}, + newShape: [3, 4, 5], + output: {dataType: 'int32', dimensions: [3, 4, 5]} + }, + { + name: + '[expand] Test with the new shapes that are broadcastable and the rank of new shapes is larger than input.', + input: {dataType: 'int32', dimensions: [2, 5]}, + newShape: [3, 2, 5], + output: {dataType: 'int32', dimensions: [3, 2, 5]} + }, + { + name: + '[expand] Throw if the input shapes are the same rank but not broadcastable.', + input: {dataType: 'uint32', dimensions: [3, 6, 2]}, + newShape: [4, 3, 5], + }, + { + name: '[expand] Throw if the input shapes are not broadcastable.', + input: {dataType: 'uint32', dimensions: [5, 4]}, + newShape: [5], + }, + { + name: '[expand] Throw if the number of new shapes is too large.', + input: {dataType: 'float32', dimensions: [1, 2, 1, 1]}, + newShape: [1, 2, kMaxUnsignedLong, kMaxUnsignedLong], + }, +]; + +tests.forEach( + test => promise_test(async t => { + const input = builder.input( + 'input', + {dataType: test.input.dataType, dimensions: test.input.dimensions}); + const options = {}; + if (test.axis) { + options.axis = test.axis; + } + + if (test.output) { + const output = builder.expand(input, test.newShape); + assert_equals(output.dataType(), test.output.dataType); + assert_array_equals(output.shape(), test.output.dimensions); + } else { + assert_throws_js(TypeError, () => builder.expand(input, test.newShape)); + } + }, test.name)); diff --git a/testing/web-platform/tests/webnn/validation_tests/gelu.https.any.js b/testing/web-platform/tests/webnn/validation_tests/gelu.https.any.js new file mode 100644 index 0000000000..c758c61f4c --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/gelu.https.any.js @@ -0,0 +1,10 @@ +// META: title=validation tests for WebNN API gelu operation +// META: global=window,dedicatedworker +// META: script=../resources/utils_validation.js + +'use strict'; + +validateInputFromAnotherBuilder('gelu'); + +validateUnaryOperation( + 'gelu', floatingPointTypes, /*alsoBuildActivation=*/ true); diff --git a/testing/web-platform/tests/webnn/validation_tests/gemm.https.any.js b/testing/web-platform/tests/webnn/validation_tests/gemm.https.any.js index 77ce6383cc..abe0ba6193 100644 --- a/testing/web-platform/tests/webnn/validation_tests/gemm.https.any.js +++ b/testing/web-platform/tests/webnn/validation_tests/gemm.https.any.js @@ -19,3 +19,143 @@ multi_builder_test(async (t, builder, otherBuilder) => { const b = builder.input('b', kExampleInputDescriptor); assert_throws_js(TypeError, () => builder.gemm(a, b, options)); }, '[gemm] throw if c option is from another builder'); + +const tests = [ + { + name: '[gemm] Test building gemm with default option.', + a: {dataType: 'float32', dimensions: [2, 3]}, + b: {dataType: 'float32', dimensions: [3, 4]}, + output: {dataType: 'float32', dimensions: [2, 4]} + }, + { + name: + '[gemm] Throw if inputShapeA[1] is not equal to inputShapeB[0] default options.', + a: {dataType: 'float32', dimensions: [2, 3]}, + b: {dataType: 'float32', dimensions: [2, 4]}, + }, + { + name: '[gemm] Test building gemm with aTranspose=true.', + a: {dataType: 'float32', dimensions: [2, 3]}, + b: {dataType: 'float32', dimensions: [2, 4]}, + options: { + aTranspose: true, + }, + output: {dataType: 'float32', dimensions: [3, 4]} + }, + { + name: + '[gemm] Throw if inputShapeA[0] is not equal to inputShapeB[0] with aTranspose=true.', + a: {dataType: 'float32', dimensions: [2, 3]}, + b: {dataType: 'float32', dimensions: [3, 4]}, + options: { + aTranspose: true, + }, + }, + { + name: '[gemm] Test building gemm with bTranspose=true.', + a: {dataType: 'float32', dimensions: [2, 3]}, + b: {dataType: 'float32', dimensions: [4, 3]}, + options: { + bTranspose: true, + }, + output: {dataType: 'float32', dimensions: [2, 4]} + }, + { + name: + '[gemm] Throw if inputShapeA[0] is not equal to inputShapeB[0] with bTranspose=true.', + a: {dataType: 'float32', dimensions: [2, 3]}, + b: {dataType: 'float32', dimensions: [3, 4]}, + options: { + bTranspose: true, + }, + }, + { + name: '[gemm] Throw if the rank of inputA is not 2.', + a: {dataType: 'float32', dimensions: [2, 3, 1]}, + b: {dataType: 'float32', dimensions: [2, 4]}, + }, + { + name: '[gemm] Throw if the rank of inputB is not 2.', + a: {dataType: 'float32', dimensions: [2, 4]}, + b: {dataType: 'float32', dimensions: [2, 3, 1]}, + }, + { + name: '[gemm] Throw if data types of two inputs do not match.', + a: {dataType: 'float32', dimensions: [2, 3]}, + b: {dataType: 'float16', dimensions: [3, 4]}, + }, + { + name: '[gemm] Test building gemm with inputC.', + a: {dataType: 'float32', dimensions: [2, 3]}, + b: {dataType: 'float32', dimensions: [3, 4]}, + options: { + c: {dataType: 'float32', dimensions: [4]}, + }, + output: {dataType: 'float32', dimensions: [2, 4]} + }, + { + name: '[gemm] Test building gemm with scalar inputC.', + a: {dataType: 'float32', dimensions: [2, 3]}, + b: {dataType: 'float32', dimensions: [3, 4]}, + options: { + c: {dataType: 'float32', dimensions: []}, + }, + output: {dataType: 'float32', dimensions: [2, 4]} + }, + { + name: + '[gemm] Throw if inputShapeC is not unidirectionally broadcastable to the output shape [inputShapeA[0], inputShapeB[1]].', + a: {dataType: 'float32', dimensions: [2, 3]}, + b: {dataType: 'float32', dimensions: [3, 4]}, + options: { + c: {dataType: 'float32', dimensions: [2, 3]}, + }, + }, + { + name: '[gemm] Throw if the input data type is not floating point.', + a: {dataType: 'int32', dimensions: [2, 3]}, + b: {dataType: 'int32', dimensions: [3, 4]} + }, + { + name: + '[gemm] Throw if data type of inputC does not match ones of inputA and inputB.', + a: {dataType: 'float32', dimensions: [3, 2]}, + b: {dataType: 'float32', dimensions: [4, 3]}, + options: { + c: {dataType: 'float16', dimensions: [2, 4]}, + aTranspose: true, + bTranspose: true, + }, + }, + { + name: '[gemm] Throw if the rank of inputC is 3.', + a: {dataType: 'float32', dimensions: [3, 2]}, + b: {dataType: 'float32', dimensions: [4, 3]}, + options: { + c: {dataType: 'float32', dimensions: [2, 3, 4]}, + aTranspose: true, + bTranspose: true, + }, + }, +]; + +tests.forEach( + test => promise_test(async t => { + const a = builder.input( + 'a', {dataType: test.a.dataType, dimensions: test.a.dimensions}); + const b = builder.input( + 'b', {dataType: test.b.dataType, dimensions: test.b.dimensions}); + if (test.options && test.options.c) { + test.options.c = builder.input('c', { + dataType: test.options.c.dataType, + dimensions: test.options.c.dimensions + }); + } + if (test.output) { + const output = builder.gemm(a, b, test.options); + assert_equals(output.dataType(), test.output.dataType); + assert_array_equals(output.shape(), test.output.dimensions); + } else { + assert_throws_js(TypeError, () => builder.gemm(a, b, test.options)); + } + }, test.name)); diff --git a/testing/web-platform/tests/webnn/validation_tests/hardSigmoid.https.any.js b/testing/web-platform/tests/webnn/validation_tests/hardSigmoid.https.any.js index 01b24dbc7c..2c55d0eb9d 100644 --- a/testing/web-platform/tests/webnn/validation_tests/hardSigmoid.https.any.js +++ b/testing/web-platform/tests/webnn/validation_tests/hardSigmoid.https.any.js @@ -5,3 +5,31 @@ 'use strict'; validateInputFromAnotherBuilder('hardSigmoid'); + +validateUnaryOperation( + 'hardSigmoid', floatingPointTypes, /*alsoBuildActivation=*/ true); + +promise_test(async t => { + const options = {alpha: 0.5, beta: 1.0}; + const input = + builder.input('input', {dataType: 'float16', dimensions: [1, 2, 3]}); + const output = builder.hardSigmoid(input, options); + assert_equals(output.dataType(), 'float16'); + assert_array_equals(output.shape(), [1, 2, 3]); +}, '[hardSigmoid] Test building an operator with options'); + +promise_test(async t => { + const options = {alpha: 0.2}; + builder.hardSigmoid(options); +}, '[hardSigmoid] Test building an activation with options'); + +promise_test(async t => { + const options = {beta: NaN}; + const input = builder.input('input', {dataType: 'float32', dimensions: []}); + assert_throws_js(TypeError, () => builder.hardSigmoid(input, options)); +}, '[hardSigmoid] Throw if options.beta is NaN when building an operator'); + +promise_test(async t => { + const options = {alpha: Infinity}; + assert_throws_js(TypeError, () => builder.hardSigmoid(options)); +}, '[hardSigmoid] Throw if options.alpha is Infinity when building an activation'); diff --git a/testing/web-platform/tests/webnn/validation_tests/instanceNormalization.https.any.js b/testing/web-platform/tests/webnn/validation_tests/instanceNormalization.https.any.js index bdd338588f..4fc26ec5ae 100644 --- a/testing/web-platform/tests/webnn/validation_tests/instanceNormalization.https.any.js +++ b/testing/web-platform/tests/webnn/validation_tests/instanceNormalization.https.any.js @@ -41,3 +41,152 @@ multi_builder_test(async (t, builder, otherBuilder) => { assert_throws_js( TypeError, () => builder.instanceNormalization(input, options)); }, '[instanceNormalization] throw if bias option is from another builder'); + +const tests = [ + { + name: '[instanceNormalization] Test with default options for 4-D input.', + input: {dataType: 'float32', dimensions: [1, 2, 3, 4]}, + output: {dataType: 'float32', dimensions: [1, 2, 3, 4]} + }, + { + name: + '[instanceNormalization] Test with scale, bias and default epsilon value.', + input: {dataType: 'float32', dimensions: [1, 2, 3, 4]}, + options: { + scale: {dataType: 'float32', dimensions: [2]}, + bias: {dataType: 'float32', dimensions: [2]}, + epsilon: 1e-5, + }, + output: {dataType: 'float32', dimensions: [1, 2, 3, 4]} + }, + { + name: '[instanceNormalization] Test with a non-default epsilon value.', + input: {dataType: 'float32', dimensions: [1, 2, 3, 4]}, + options: { + epsilon: 1e-4, + }, + output: {dataType: 'float32', dimensions: [1, 2, 3, 4]} + }, + { + name: '[instanceNormalization] Test with layout=nhwc.', + input: {dataType: 'float32', dimensions: [1, 2, 3, 4]}, + options: { + layout: 'nhwc', + scale: {dataType: 'float32', dimensions: [4]}, + bias: {dataType: 'float32', dimensions: [4]}, + }, + output: {dataType: 'float32', dimensions: [1, 2, 3, 4]} + }, + { + name: '[instanceNormalization] Test when the input data type is float16.', + input: {dataType: 'float16', dimensions: [1, 2, 3, 4]}, + output: {dataType: 'float16', dimensions: [1, 2, 3, 4]} + }, + { + name: '[instanceNormalization] Throw if the input is not a 4-D tensor.', + input: {dataType: 'float32', dimensions: [1, 2, 5, 5, 2]}, + }, + { + name: + '[instanceNormalization] Throw if the input data type is not one of floating point types.', + input: {dataType: 'int32', dimensions: [1, 2, 5, 5]}, + }, + { + name: + '[instanceNormalization] Throw if the scale data type is not the same as the input data type.', + input: {dataType: 'float16', dimensions: [1, 2, 5, 5]}, + options: { + scale: {dataType: 'float32', dimensions: [2]}, + }, + }, + { + name: + '[instanceNormalization] Throw if the scale operand is not a 1-D tensor.', + input: {dataType: 'float32', dimensions: [1, 2, 5, 5]}, + options: { + scale: {dataType: 'float32', dimensions: [2, 1]}, + }, + }, + { + name: + '[instanceNormalization] Throw if the size of scale operand is not equal to the size of the feature dimension of the input with layout=nhwc.', + input: {dataType: 'float32', dimensions: [1, 2, 5, 5]}, + options: { + layout: 'nhwc', + scale: {dataType: 'float32', dimensions: [2]}, + }, + }, + { + name: + '[instanceNormalization] Throw if the size of scale operand is not equal to the size of the feature dimension of the input with layout=nchw.', + input: {dataType: 'float32', dimensions: [1, 5, 5, 2]}, + options: { + layout: 'nchw', + scale: {dataType: 'float32', dimensions: [2]}, + }, + }, + { + name: + '[instanceNormalization] Throw if the bias data type is not the same as the input data type.', + input: {dataType: 'float16', dimensions: [1, 2, 5, 5]}, + options: { + bias: {dataType: 'float32', dimensions: [2]}, + }, + }, + { + name: + '[instanceNormalization] Throw if the bias operand is not a 1-D tensor.', + input: {dataType: 'float32', dimensions: [1, 2, 5, 5]}, + options: { + scale: {dataType: 'float32', dimensions: [2, 1]}, + }, + }, + { + name: + '[instanceNormalization] Throw if the size of bias operand is not equal to the size of the feature dimension of the input with layout=nhwc.', + input: {dataType: 'float32', dimensions: [1, 2, 5, 5]}, + options: { + layout: 'nhwc', + bias: {dataType: 'float32', dimensions: [2]}, + }, + }, + { + name: + '[instanceNormalization] Throw if the size of bias operand is not equal to the size of the feature dimension of the input with layout=nchw.', + input: {dataType: 'float32', dimensions: [1, 5, 5, 2]}, + options: { + layout: 'nchw', + bias: {dataType: 'float32', dimensions: [2]}, + }, + }, +]; + +tests.forEach( + test => promise_test(async t => { + const input = builder.input( + 'input', + {dataType: test.input.dataType, dimensions: test.input.dimensions}); + + if (test.options && test.options.bias) { + test.options.bias = builder.input('bias', { + dataType: test.options.bias.dataType, + dimensions: test.options.bias.dimensions + }); + } + if (test.options && test.options.scale) { + test.options.scale = builder.input('scale', { + dataType: test.options.scale.dataType, + dimensions: test.options.scale.dimensions + }); + } + + if (test.output) { + const output = builder.instanceNormalization(input, test.options); + assert_equals(output.dataType(), test.output.dataType); + assert_array_equals(output.shape(), test.output.dimensions); + } else { + assert_throws_js( + TypeError, + () => builder.instanceNormalization(input, test.options)); + } + }, test.name)); diff --git a/testing/web-platform/tests/webnn/validation_tests/layerNormalization.https.any.js b/testing/web-platform/tests/webnn/validation_tests/layerNormalization.https.any.js index e9e9141aa6..63f9c0dbc5 100644 --- a/testing/web-platform/tests/webnn/validation_tests/layerNormalization.https.any.js +++ b/testing/web-platform/tests/webnn/validation_tests/layerNormalization.https.any.js @@ -9,8 +9,6 @@ const kExampleInputDescriptor = { dimensions: [2, 2] }; -validateOptionsAxes('layerNormalization', 4); - validateInputFromAnotherBuilder('layerNormalization'); multi_builder_test(async (t, builder, otherBuilder) => { @@ -30,3 +28,181 @@ multi_builder_test(async (t, builder, otherBuilder) => { const input = builder.input('input', kExampleInputDescriptor); assert_throws_js(TypeError, () => builder.layerNormalization(input, options)); }, '[layerNormalization] throw if bias option is from another builder'); + +const tests = [ + { + name: '[layerNormalization] Test with default options for scalar input.', + input: {dataType: 'float32', dimensions: []}, + output: {dataType: 'float32', dimensions: []}, + }, + { + name: '[layerNormalization] Test when the input data type is float16.', + input: {dataType: 'float16', dimensions: []}, + output: {dataType: 'float16', dimensions: []}, + }, + { + name: '[layerNormalization] Test with given axes.', + input: {dataType: 'float32', dimensions: [1, 2, 3, 4]}, + options: { + axes: [3], + }, + output: {dataType: 'float32', dimensions: [1, 2, 3, 4]}, + }, + { + name: '[layerNormalization] Test with given scale.', + input: {dataType: 'float32', dimensions: [1, 2, 3, 4]}, + options: { + scale: {dataType: 'float32', dimensions: [2, 3, 4]}, + }, + output: {dataType: 'float32', dimensions: [1, 2, 3, 4]}, + }, + { + name: '[layerNormalization] Test with a non-default epsilon value.', + input: {dataType: 'float32', dimensions: [1, 2, 3, 4]}, + options: { + epsilon: 1e-4, // default epsilon=1e-5 + }, + output: {dataType: 'float32', dimensions: [1, 2, 3, 4]}, + }, + { + name: '[layerNormalization] Test with given axes, scale and bias.', + input: {dataType: 'float32', dimensions: [1, 2, 3, 4]}, + options: { + scale: {dataType: 'float32', dimensions: [3, 4]}, + bias: {dataType: 'float32', dimensions: [3, 4]}, + axes: [2, 3], + }, + output: {dataType: 'float32', dimensions: [1, 2, 3, 4]}, + }, + { + name: '[layerNormalization] Test with nonconsecutive axes.', + input: {dataType: 'float32', dimensions: [1, 2, 3, 4, 5, 6]}, + options: { + scale: {dataType: 'float32', dimensions: [2, 4, 6]}, + bias: {dataType: 'float32', dimensions: [2, 4, 6]}, + axes: [1, 3, 5], + }, + output: {dataType: 'float32', dimensions: [1, 2, 3, 4, 5, 6]}, + }, + { + name: '[layerNormalization] Test with axes in descending order.', + input: {dataType: 'float32', dimensions: [1, 2, 3, 4, 5, 6]}, + options: { + scale: {dataType: 'float32', dimensions: [6, 5, 4, 3, 2]}, + bias: {dataType: 'float32', dimensions: [6, 5, 4, 3, 2]}, + axes: [5, 4, 3, 2, 1] + }, + output: {dataType: 'float32', dimensions: [1, 2, 3, 4, 5, 6]}, + }, + { + name: + '[layerNormalization] Throw if the input data type is not one of the floating point types.', + input: {dataType: 'uint32', dimensions: [1, 2, 3, 4]}, + }, + { + name: + '[layerNormalization] Throw if the axis is greater than the input rank.', + input: {dataType: 'float32', dimensions: [1, 2, 3, 4]}, + options: { + axes: [1, 2, 4], + }, + }, + { + name: '[layerNormalization] Throw if the axes have duplications.', + input: {dataType: 'float32', dimensions: [1, 2, 3, 4]}, + options: {axes: [3, 3]}, + }, + { + name: + '[layerNormalization] Throw if the bias data type doesn\'t match input data type', + input: {dataType: 'float32', dimensions: [1, 2, 3, 4]}, + options: { + scale: {dataType: 'float32', dimensions: [3, 4]}, + bias: {dataType: 'float16', dimensions: [3, 4]}, + axes: [2, 3], + }, + }, + { + name: + '[layerNormalization] Throw if the scale data type doesn\'t match input data type', + input: {dataType: 'float32', dimensions: [1, 2, 3, 4]}, + options: { + scale: {dataType: 'float16', dimensions: [3, 4]}, + bias: {dataType: 'float32', dimensions: [3, 4]}, + axes: [2, 3], + }, + }, + { + name: + '[layerNormalization] Throw if the bias dimensions doesn\'t match axis dimensions.', + input: {dataType: 'float32', dimensions: [1, 2, 3, 4]}, + options: { + bias: { + dataType: 'float32', + dimensions: [3, 3, 4] + }, // for 4D input, default axes = [1,2,3] + }, + }, + { + name: + '[layerNormalization] Throw if the scale dimensions doesn\'t match axis dimensions.', + input: {dataType: 'float32', dimensions: [1, 2, 3, 4]}, + options: { + scale: { + dataType: 'float32', + dimensions: [3, 3, 4] + }, // for 4D input, default axes = [1,2,3] + }, + }, + { + name: + '[layerNormalization] Throw if the bias rank doesn\'t match axis rank.', + input: {dataType: 'float32', dimensions: [1, 2, 3, 4]}, + options: { + bias: { + dataType: 'float32', + dimensions: [1, 2, 3, 4] + }, // for 4D input, default axes = [1,2,3] + }, + }, + { + name: + '[layerNormalization] Throw if the scale rank doesn\'t match axis rank.', + input: {dataType: 'float32', dimensions: [1, 2, 3, 4]}, + options: { + scale: { + dataType: 'float32', + dimensions: [1, 2, 3, 4] + }, // for 4D input, default axes = [1,2,3] + }, + }, +]; + +tests.forEach( + test => promise_test(async t => { + const input = builder.input( + 'input', + {dataType: test.input.dataType, dimensions: test.input.dimensions}); + + if (test.options && test.options.bias) { + test.options.bias = builder.input('bias', { + dataType: test.options.bias.dataType, + dimensions: test.options.bias.dimensions + }); + } + if (test.options && test.options.scale) { + test.options.scale = builder.input('scale', { + dataType: test.options.scale.dataType, + dimensions: test.options.scale.dimensions + }); + } + + if (test.output) { + const output = builder.layerNormalization(input, test.options); + assert_equals(output.dataType(), test.output.dataType); + assert_array_equals(output.shape(), test.output.dimensions); + } else { + assert_throws_js( + TypeError, () => builder.layerNormalization(input, test.options)); + } + }, test.name)); diff --git a/testing/web-platform/tests/webnn/validation_tests/leakyRelu.https.any.js b/testing/web-platform/tests/webnn/validation_tests/leakyRelu.https.any.js index 6fc19b1f0d..f250b0eda6 100644 --- a/testing/web-platform/tests/webnn/validation_tests/leakyRelu.https.any.js +++ b/testing/web-platform/tests/webnn/validation_tests/leakyRelu.https.any.js @@ -5,3 +5,31 @@ 'use strict'; validateInputFromAnotherBuilder('leakyRelu'); + +validateUnaryOperation( + 'leakyRelu', floatingPointTypes, /*alsoBuildActivation=*/ true); + +promise_test(async t => { + const options = {alpha: 0.02}; + const input = + builder.input('input', {dataType: 'float32', dimensions: [1, 2, 3]}); + const output = builder.leakyRelu(input, options); + assert_equals(output.dataType(), 'float32'); + assert_array_equals(output.shape(), [1, 2, 3]); +}, '[leakyRelu] Test building an operator with options'); + +promise_test(async t => { + const options = {alpha: 0.03}; + builder.leakyRelu(options); +}, '[leakyRelu] Test building an activation with options'); + +promise_test(async t => { + const options = {alpha: Infinity}; + const input = builder.input('input', {dataType: 'float16', dimensions: []}); + assert_throws_js(TypeError, () => builder.leakyRelu(input, options)); +}, '[leakyRelu] Throw if options.alpha is Infinity when building an operator'); + +promise_test(async t => { + const options = {alpha: -NaN}; + assert_throws_js(TypeError, () => builder.leakyRelu(options)); +}, '[leakyRelu] Throw if options.alpha is -NaN when building an activation'); diff --git a/testing/web-platform/tests/webnn/validation_tests/linear.https.any.js b/testing/web-platform/tests/webnn/validation_tests/linear.https.any.js index 99c1daad3f..6ec0389fc3 100644 --- a/testing/web-platform/tests/webnn/validation_tests/linear.https.any.js +++ b/testing/web-platform/tests/webnn/validation_tests/linear.https.any.js @@ -5,3 +5,31 @@ 'use strict'; validateInputFromAnotherBuilder('linear'); + +validateUnaryOperation( + 'linear', floatingPointTypes, /*alsoBuildActivation=*/ true); + +promise_test(async t => { + const options = {alpha: 1.5, beta: 0.3}; + const input = + builder.input('input', {dataType: 'float32', dimensions: [1, 2, 3]}); + const output = builder.linear(input, options); + assert_equals(output.dataType(), 'float32'); + assert_array_equals(output.shape(), [1, 2, 3]); +}, '[linear] Test building an operator with options'); + +promise_test(async t => { + const options = {beta: 1.5}; + builder.linear(options); +}, '[linear] Test building an activation with options'); + +promise_test(async t => { + const options = {beta: -Infinity}; + const input = builder.input('input', {dataType: 'float16', dimensions: []}); + assert_throws_js(TypeError, () => builder.linear(input, options)); +}, '[linear] Throw if options.beta is -Infinity when building an operator'); + +promise_test(async t => { + const options = {alpha: NaN}; + assert_throws_js(TypeError, () => builder.linear(options)); +}, '[linear] Throw if options.alpha is NaN when building an activation'); diff --git a/testing/web-platform/tests/webnn/validation_tests/matmul.https.any.js b/testing/web-platform/tests/webnn/validation_tests/matmul.https.any.js index 03616ddb01..8db16242c9 100644 --- a/testing/web-platform/tests/webnn/validation_tests/matmul.https.any.js +++ b/testing/web-platform/tests/webnn/validation_tests/matmul.https.any.js @@ -5,3 +5,116 @@ 'use strict'; validateTwoInputsFromMultipleBuilders('matmul'); + +const tests = [ + { + name: '[matmul] Throw if first input\'s rank is less than 2', + inputs: { + a: {dataType: 'float32', dimensions: [2]}, + b: {dataType: 'float32', dimensions: [2, 2]} + } + }, + { + name: '[matmul] Throw if second input\'s rank is less than 2', + inputs: { + a: {dataType: 'float32', dimensions: [2, 2]}, + b: {dataType: 'float32', dimensions: [2]} + } + }, + { + name: '[matmul] Test with 2-D input and 4-D input', + inputs: { + a: {dataType: 'float32', dimensions: [1, 4]}, + b: {dataType: 'float32', dimensions: [2, 2, 4, 2]} + }, + output: {dataType: 'float32', dimensions: [2, 2, 1, 2]} + }, + { + name: '[matmul] Test with 2-D input and 2-D input', + inputs: { + a: {dataType: 'float32', dimensions: [4, 2]}, + b: {dataType: 'float32', dimensions: [2, 3]} + }, + output: {dataType: 'float32', dimensions: [4, 3]} + }, + { + // batchShape is a clone of inputShape with the spatial dimensions + // (last 2 items) removed. + name: + '[matmul] Test with 3-D input and 3-D input of broadcastable batchShape', + inputs: { + a: {dataType: 'float32', dimensions: [2, 3, 4]}, + b: {dataType: 'float32', dimensions: [1, 4, 1]} + }, + output: {dataType: 'float32', dimensions: [2, 3, 1]} + }, + { + // batchShape is a clone of inputShape with the spatial dimensions + // (last 2 items) removed. + name: + '[matmul] Test with 4-D input and 3-D input of broadcastable batchShape', + inputs: { + a: {dataType: 'float32', dimensions: [2, 2, 3, 4]}, + b: {dataType: 'float32', dimensions: [1, 4, 5]} + }, + output: {dataType: 'float32', dimensions: [2, 2, 3, 5]} + }, + { + name: '[matmul] Test with 3-D input and 3-D input', + inputs: { + a: {dataType: 'float32', dimensions: [2, 3, 4]}, + b: {dataType: 'float32', dimensions: [2, 4, 5]} + }, + output: {dataType: 'float32', dimensions: [2, 3, 5]} + }, + { + name: '[matmul] Throw if the input data type is not floating point', + inputs: { + a: {dataType: 'uint32', dimensions: [2, 3, 4]}, + b: {dataType: 'uint32', dimensions: [2, 4, 5]} + } + }, + { + name: '[matmul] Throw if data type of two inputs don\'t match', + inputs: { + a: {dataType: 'float32', dimensions: [2, 3, 4]}, + b: {dataType: 'float16', dimensions: [2, 4, 5]} + } + }, + { + name: + '[matmul] Throw if columns of first input\'s shape doesn\'t match the rows of second input\'s shape', + inputs: { + a: {dataType: 'float32', dimensions: /* [rows, columns] */[2, 3]}, + b: {dataType: 'float32', dimensions: /* [rows, columns] */[2, 4]} + }, + }, + { + // batchShape is a clone of inputShape with the spatial dimensions + // (last 2 items) removed. + name: '[matmul] Throw if batchShapes aren\'t bidirectionally broadcastable', + inputs: { + a: {dataType: 'float32', dimensions: [3, 3, 4]}, + b: {dataType: 'float32', dimensions: [2, 4, 1]} + }, + }, +]; + +tests.forEach(test => promise_test(async t => { + const inputA = builder.input('a', { + dataType: test.inputs.a.dataType, + dimensions: test.inputs.a.dimensions + }); + const inputB = builder.input('b', { + dataType: test.inputs.b.dataType, + dimensions: test.inputs.b.dimensions + }); + if (test.output) { + const output = builder.matmul(inputA, inputB); + assert_equals(output.dataType(), test.output.dataType); + assert_array_equals(output.shape(), test.output.dimensions); + } else { + assert_throws_js( + TypeError, () => builder.matmul(inputA, inputB)); + } + }, test.name)); diff --git a/testing/web-platform/tests/webnn/validation_tests/pad.https.any.js b/testing/web-platform/tests/webnn/validation_tests/pad.https.any.js index 11c6a8f7ef..cc39bee4c0 100644 --- a/testing/web-platform/tests/webnn/validation_tests/pad.https.any.js +++ b/testing/web-platform/tests/webnn/validation_tests/pad.https.any.js @@ -15,3 +15,73 @@ multi_builder_test(async (t, builder, otherBuilder) => { () => builder.pad(inputFromOtherBuilder, beginningPadding, endingPadding)); }, '[pad] throw if input is from another builder'); + +const tests = [ + { + name: + '[pad] Test with default options, beginningPadding=[1, 2] and endingPadding=[1, 2].', + input: {dataType: 'float32', dimensions: [2, 3]}, + beginningPadding: [1, 2], + endingPadding: [1, 2], + options: { + mode: 'constant', + value: 0, + }, + output: {dataType: 'float32', dimensions: [4, 7]} + }, + { + name: '[pad] Throw if building pad for scalar input.', + input: {dataType: 'float32', dimensions: []}, + beginningPadding: [], + endingPadding: [], + }, + { + name: + '[pad] Throw if the length of beginningPadding is not equal to the input rank.', + input: {dataType: 'float32', dimensions: [2, 3]}, + beginningPadding: [1], + endingPadding: [1, 2], + options: { + mode: 'edge', + value: 0, + }, + }, + { + name: + '[pad] Throw if the length of endingPadding is not equal to the input rank.', + input: {dataType: 'float32', dimensions: [2, 3]}, + beginningPadding: [1, 0], + endingPadding: [1, 2, 0], + options: { + mode: 'reflection', + }, + }, + { + name: '[pad] Throw if the padding of one dimension is too large.', + input: {dataType: 'float32', dimensions: [2, 3]}, + beginningPadding: [2294967295, 0], + endingPadding: [3294967295, 2], + options: { + mode: 'reflection', + }, + }, +]; + +tests.forEach( + test => promise_test(async t => { + const input = builder.input( + 'input', + {dataType: test.input.dataType, dimensions: test.input.dimensions}); + if (test.output) { + const output = builder.pad( + input, test.beginningPadding, test.endingPadding, test.options); + assert_equals(output.dataType(), test.output.dataType); + assert_array_equals(output.shape(), test.output.dimensions); + } else { + assert_throws_js( + TypeError, + () => builder.pad( + input, test.beginningPadding, test.endingPadding, + test.options)); + } + }, test.name)); diff --git a/testing/web-platform/tests/webnn/validation_tests/pooling-and-reduction-keep-dims.https.any.js b/testing/web-platform/tests/webnn/validation_tests/pooling-and-reduction-keep-dims.https.any.js new file mode 100644 index 0000000000..9f6b9fb338 --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/pooling-and-reduction-keep-dims.https.any.js @@ -0,0 +1,94 @@ +// META: title=validation tests for pooling and reduction operators keep dimensions +// META: global=window,dedicatedworker +// META: script=../resources/utils.js +// META: script=../resources/utils_validation.js +// META: timeout=long + +'use strict'; + +// This is used to reproduce an issue(crbug.com/331841268) of averagePool2d in +// ResNetV2 50 model. +// [input] +// | +// [globalAveragePool] +// | +// [conv2d] +// | +// [reshape] +// | +// [output] +promise_test(async t => { + const avgPool2dInputShape = [1, 7, 7, 2048]; + const avgPool2dInput = builder.input( + `avgPool2dInput`, {dataType: 'float32', dimensions: avgPool2dInputShape}); + const avgPool2dOutput = + builder.averagePool2d(avgPool2dInput, {layout: 'nhwc'}); + const conv2dFilterShape = [1001, 1, 1, 2048]; + const conv2dFilter = builder.constant( + {dataType: 'float32', dimensions: conv2dFilterShape}, + new Float32Array(sizeOfShape(conv2dFilterShape)).fill(1)); + const conv2dBias = builder.constant( + {dataType: 'float32', dimensions: [1001]}, + new Float32Array(1001).fill(0.01)); + const conv2dOutput = builder.conv2d(avgPool2dOutput, conv2dFilter, { + inputLayout: 'nhwc', + filterLayout: 'ohwi', + padding: [0, 0, 0, 0], + bias: conv2dBias + }); + const newShape = [1, 1001]; + const reshapeOutput = builder.reshape(conv2dOutput, newShape); + assert_equals(reshapeOutput.dataType(), avgPool2dInput.dataType()); + assert_array_equals(reshapeOutput.shape(), newShape); + const graph = await builder.build({reshapeOutput}); + const result = await context.compute( + graph, { + 'avgPool2dInput': + new Float32Array(sizeOfShape(avgPool2dInputShape)).fill(0.1) + }, + {'reshapeOutput': new Float32Array(1001)}); +}, 'Test global average pool operator\'s output shape for ResNetV2 50 model.'); + +// This is used to reproduce an issue(crbug.com/331841268) of reduceMean in +// ResNetV2 50 model. +// [input] +// | +// [reduceMean] +// | +// [conv2d] +// | +// [reshape] +// | +// [output] +promise_test(async t => { + const reduceMeanInputShape = [1, 7, 7, 2048]; + const reduceMeanInput = builder.input( + `reduceMeanInput`, + {dataType: 'float32', dimensions: reduceMeanInputShape}); + const reduceMeanOutput = + builder.reduceMean(reduceMeanInput, {axes: [1, 2], keepDimensions: true}); + const conv2dFilterShape = [1001, 1, 1, 2048]; + const conv2dFilter = builder.constant( + {dataType: 'float32', dimensions: conv2dFilterShape}, + new Float32Array(sizeOfShape(conv2dFilterShape)).fill(1)); + const conv2dBias = builder.constant( + {dataType: 'float32', dimensions: [1001]}, + new Float32Array(1001).fill(0.01)); + const conv2dOutput = builder.conv2d(reduceMeanOutput, conv2dFilter, { + inputLayout: 'nhwc', + filterLayout: 'ohwi', + padding: [0, 0, 0, 0], + bias: conv2dBias + }); + const newShape = [1, 1001]; + const reshapeOutput = builder.reshape(conv2dOutput, newShape); + assert_equals(reshapeOutput.dataType(), reduceMeanInput.dataType()); + assert_array_equals(reshapeOutput.shape(), newShape); + const graph = await builder.build({reshapeOutput}); + const result = await context.compute( + graph, { + 'reduceMeanInput': + new Float32Array(sizeOfShape(reduceMeanInputShape)).fill(0.1) + }, + {'reshapeOutput': new Float32Array(1001)}); +}, 'Test reduceMean operator\'s output shape for ResNetV2 50 model.'); diff --git a/testing/web-platform/tests/webnn/validation_tests/reshape.https.any.js b/testing/web-platform/tests/webnn/validation_tests/reshape.https.any.js index 435551b716..67491fbc16 100644 --- a/testing/web-platform/tests/webnn/validation_tests/reshape.https.any.js +++ b/testing/web-platform/tests/webnn/validation_tests/reshape.https.any.js @@ -12,3 +12,68 @@ multi_builder_test(async (t, builder, otherBuilder) => { assert_throws_js( TypeError, () => builder.reshape(inputFromOtherBuilder, newShape)); }, '[reshape] throw if input is from another builder'); + +const tests = [ + { + name: '[reshape] Test with new shape=[3, 8].', + input: {dataType: 'float32', dimensions: [2, 3, 4]}, + newShape: [3, 8], + output: {dataType: 'float32', dimensions: [3, 8]} + }, + { + name: '[reshape] Test with new shape=[24], src shape=[2, 3, 4].', + input: {dataType: 'float32', dimensions: [2, 3, 4]}, + newShape: [24], + output: {dataType: 'float32', dimensions: [24]} + }, + { + name: '[reshape] Test with new shape=[1], src shape=[1].', + input: {dataType: 'float32', dimensions: [1]}, + newShape: [1], + output: {dataType: 'float32', dimensions: [1]} + }, + { + name: '[reshape] Test reshaping a 1-D 1-element tensor to scalar.', + input: {dataType: 'float32', dimensions: [1]}, + newShape: [], + output: {dataType: 'float32', dimensions: []} + }, + { + name: '[reshape] Test reshaping a scalar to 1-D 1-element tensor.', + input: {dataType: 'float32', dimensions: []}, + newShape: [1], + output: {dataType: 'float32', dimensions: [1]} + }, + { + name: '[reshape] Throw if one value of new shape is 0.', + input: {dataType: 'float32', dimensions: [2, 4]}, + newShape: [2, 4, 0], + }, + { + name: + '[reshape] Throw if the number of elements implied by new shape is not equal to the number of elements in the input tensor when new shape=[].', + input: {dataType: 'float32', dimensions: [2, 3, 4]}, + newShape: [], + }, + { + name: + '[reshape] Throw if the number of elements implied by new shape is not equal to the number of elements in the input tensor.', + input: {dataType: 'float32', dimensions: [2, 3, 4]}, + newShape: [3, 9], + }, +]; + +tests.forEach( + test => promise_test(async t => { + const input = builder.input( + 'input', + {dataType: test.input.dataType, dimensions: test.input.dimensions}); + if (test.output) { + const output = builder.reshape(input, test.newShape); + assert_equals(output.dataType(), test.output.dataType); + assert_array_equals(output.shape(), test.output.dimensions); + } else { + assert_throws_js( + TypeError, () => builder.reshape(input, test.newShape)); + } + }, test.name)); diff --git a/testing/web-platform/tests/webnn/validation_tests/slice.https.any.js b/testing/web-platform/tests/webnn/validation_tests/slice.https.any.js index a45ecd3fcb..de42621610 100644 --- a/testing/web-platform/tests/webnn/validation_tests/slice.https.any.js +++ b/testing/web-platform/tests/webnn/validation_tests/slice.https.any.js @@ -13,3 +13,69 @@ multi_builder_test(async (t, builder, otherBuilder) => { assert_throws_js( TypeError, () => builder.slice(inputFromOtherBuilder, starts, sizes)); }, '[slice] throw if input is from another builder'); + +const tests = [ + { + name: '[slice] Test with starts=[0, 1, 2] and sizes=[1, 2, 3].', + input: {dataType: 'float32', dimensions: [3, 4, 5]}, + starts: [0, 1, 2], + sizes: [1, 2, 3], + output: {dataType: 'float32', dimensions: [1, 2, 3]} + }, + { + name: '[slice] Throw if input is a scalar.', + input: {dataType: 'float32', dimensions: []}, + starts: [0], + sizes: [1] + }, + { + name: + '[slice] Throw if the length of sizes is not equal to the rank of the input tensor.', + input: {dataType: 'float32', dimensions: [3, 4, 5]}, + starts: [1, 2, 3], + sizes: [1, 1] + }, + { + name: + '[slice] Throw if the length of starts is not equal to the rank of the input tensor.', + input: {dataType: 'float32', dimensions: [3, 4, 5]}, + starts: [1, 2, 1, 3], + sizes: [1, 1, 1] + }, + { + name: + '[slice] Throw if the starting index is equal to or greater than input size in the same dimension.', + input: {dataType: 'float32', dimensions: [3, 4, 5]}, + starts: [0, 4, 4], + sizes: [1, 1, 1] + }, + { + name: '[slice] Throw if the number of elements to slice is equal to 0.', + input: {dataType: 'float32', dimensions: [3, 4, 5]}, + starts: [1, 2, 3], + sizes: [1, 0, 1] + }, + { + name: + '[slice] Throw if the ending index to slice is greater than input size in the same dimension.', + input: {dataType: 'float32', dimensions: [3, 4, 5]}, + starts: [0, 1, 2], + sizes: [3, 4, 1] + }, +]; + +tests.forEach( + test => promise_test(async t => { + const input = builder.input( + 'input', + {dataType: test.input.dataType, dimensions: test.input.dimensions}); + + if (test.output) { + const output = builder.slice(input, test.starts, test.sizes); + assert_equals(output.dataType(), test.output.dataType); + assert_array_equals(output.shape(), test.output.dimensions); + } else { + assert_throws_js( + TypeError, () => builder.slice(input, test.starts, test.sizes)); + } + }, test.name)); diff --git a/testing/web-platform/tests/webnn/validation_tests/softplus.https.any.js b/testing/web-platform/tests/webnn/validation_tests/softplus.https.any.js index 347dfcd938..3cf91d26ec 100644 --- a/testing/web-platform/tests/webnn/validation_tests/softplus.https.any.js +++ b/testing/web-platform/tests/webnn/validation_tests/softplus.https.any.js @@ -5,3 +5,6 @@ 'use strict'; validateInputFromAnotherBuilder('softplus'); + +validateUnaryOperation( + 'softplus', floatingPointTypes, /*alsoBuildActivation=*/ true); diff --git a/testing/web-platform/tests/webnn/validation_tests/split.https.any.js b/testing/web-platform/tests/webnn/validation_tests/split.https.any.js index 38f3126603..6f7809744a 100644 --- a/testing/web-platform/tests/webnn/validation_tests/split.https.any.js +++ b/testing/web-platform/tests/webnn/validation_tests/split.https.any.js @@ -12,3 +12,83 @@ multi_builder_test(async (t, builder, otherBuilder) => { assert_throws_js( TypeError, () => builder.split(inputFromOtherBuilder, splits)); }, '[split] throw if input is from another builder'); + +const tests = [ + { + name: '[split] Test with default options.', + input: {dataType: 'float32', dimensions: [2, 6]}, + splits: [2], + outputs: [ + {dataType: 'float32', dimensions: [2, 6]}, + ] + }, + { + name: + '[split] Test with a sequence of unsigned long splits and with options.axis = 1.', + input: {dataType: 'float32', dimensions: [2, 6]}, + splits: [1, 2, 3], + options: {axis: 1}, + outputs: [ + {dataType: 'float32', dimensions: [2, 1]}, + {dataType: 'float32', dimensions: [2, 2]}, + {dataType: 'float32', dimensions: [2, 3]}, + ] + }, + { + name: '[split] Throw if splitting a scalar.', + input: {dataType: 'float32', dimensions: []}, + splits: [2], + }, + { + name: '[split] Throw if axis is larger than input rank.', + input: {dataType: 'float32', dimensions: [2, 6]}, + splits: [2], + options: { + axis: 2, + } + }, + { + name: '[split] Throw if splits is equal to 0.', + input: {dataType: 'float32', dimensions: [2, 6]}, + splits: [0], + options: { + axis: 2, + } + }, + { + name: + '[split] Throw if the splits can not evenly divide the dimension size of input along options.axis.', + input: {dataType: 'float32', dimensions: [2, 5]}, + splits: [2], + options: { + axis: 1, + } + }, + { + name: + '[split] Throw if the sum of splits sizes not equal to the dimension size of input along options.axis.', + input: {dataType: 'float32', dimensions: [2, 6]}, + splits: [2, 2, 3], + options: { + axis: 1, + } + }, +]; + +tests.forEach( + test => promise_test(async t => { + const input = builder.input( + 'input', + {dataType: test.input.dataType, dimensions: test.input.dimensions}); + if (test.outputs) { + const outputs = builder.split(input, test.splits, test.options); + assert_equals(outputs.length, test.outputs.length); + for (let i = 0; i < outputs.length; ++i) { + assert_equals(outputs[i].dataType(), test.outputs[i].dataType); + assert_array_equals(outputs[i].shape(), test.outputs[i].dimensions); + } + } else { + assert_throws_js( + TypeError, () => builder.split(input, test.splits, test.options)); + } + }, test.name)); diff --git a/testing/web-platform/tests/webnn/validation_tests/transpose.https.any.js b/testing/web-platform/tests/webnn/validation_tests/transpose.https.any.js index 9ea5a5dcf8..3475a427d7 100644 --- a/testing/web-platform/tests/webnn/validation_tests/transpose.https.any.js +++ b/testing/web-platform/tests/webnn/validation_tests/transpose.https.any.js @@ -5,3 +5,54 @@ 'use strict'; validateInputFromAnotherBuilder('transpose'); + +const tests = [ + { + name: '[transpose] Test building transpose with default options.', + input: {dataType: 'float32', dimensions: [1, 2, 3, 4]}, + output: {dataType: 'float32', dimensions: [4, 3, 2, 1]} + }, + { + name: '[transpose] Test building transpose with permutation=[0, 2, 3, 1].', + input: {dataType: 'float32', dimensions: [1, 2, 3, 4]}, + options: {permutation: [0, 2, 3, 1]}, + output: {dataType: 'float32', dimensions: [1, 3, 4, 2]} + }, + { + name: + '[transpose] Throw if permutation\'s size is not the same as input\'s rank.', + input: {dataType: 'int32', dimensions: [1, 2, 4]}, + options: {permutation: [0, 2, 3, 1]}, + }, + { + name: '[transpose] Throw if two values in permutation are same.', + input: {dataType: 'int32', dimensions: [1, 2, 3, 4]}, + options: {permutation: [0, 2, 3, 2]}, + }, + { + name: + '[transpose] Throw if any value in permutation is not in the range [0,input\'s rank).', + input: {dataType: 'int32', dimensions: [1, 2, 3, 4]}, + options: {permutation: [0, 1, 2, 4]}, + }, + { + name: '[transpose] Throw if any value in permutation is negative.', + input: {dataType: 'int32', dimensions: [1, 2, 3, 4]}, + options: {permutation: [0, -1, 2, 3]}, + } +]; + +tests.forEach( + test => promise_test(async t => { + const input = builder.input( + 'input', + {dataType: test.input.dataType, dimensions: test.input.dimensions}); + if (test.output) { + const output = builder.transpose(input, test.options); + assert_equals(output.dataType(), test.output.dataType); + assert_array_equals(output.shape(), test.output.dimensions); + } else { + assert_throws_js( + TypeError, () => builder.transpose(input, test.options)); + } + }, test.name)); -- cgit v1.2.3