diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:13:33 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:13:33 +0000 |
commit | 086c044dc34dfc0f74fbe41f4ecb402b2cd34884 (patch) | |
tree | a4f824bd33cb075dd5aa3eb5a0a94af221bbe83a /testing/web-platform/tests/webnn/validation_tests | |
parent | Adding debian version 124.0.1-1. (diff) | |
download | firefox-086c044dc34dfc0f74fbe41f4ecb402b2cd34884.tar.xz firefox-086c044dc34dfc0f74fbe41f4ecb402b2cd34884.zip |
Merging upstream version 125.0.1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/webnn/validation_tests')
10 files changed, 1108 insertions, 0 deletions
diff --git a/testing/web-platform/tests/webnn/validation_tests/arg_min_max.https.any.js b/testing/web-platform/tests/webnn/validation_tests/arg_min_max.https.any.js new file mode 100644 index 0000000000..700be83b04 --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/arg_min_max.https.any.js @@ -0,0 +1,8 @@ +// META: title=validation tests for WebNN API argMin/Max operations +// META: global=window,dedicatedworker +// META: script=../resources/utils_validation.js +// META: timeout=long + +'use strict'; + +validateOptionsAxes(['argMin', 'argMax']); diff --git a/testing/web-platform/tests/webnn/validation_tests/batch_normalization.https.any.js b/testing/web-platform/tests/webnn/validation_tests/batch_normalization.https.any.js new file mode 100644 index 0000000000..25b542d34e --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/batch_normalization.https.any.js @@ -0,0 +1,190 @@ +// META: title=validation tests for WebNN API batchNormalization operation +// META: global=window,dedicatedworker +// META: script=../resources/utils_validation.js +// META: timeout=long + +'use strict'; + +let meanIndex = 0; +let varianceIndex = 0; + +promise_test(async t => { + for (let dataType of allWebNNOperandDataTypes) { + const input = builder.input(`input${++inputIndex}`, {dataType, dimensions: dimensions2D}); + const validAxisArray = getAxisArray(dimensions2D); + const invalidAxisArray = generateOutOfRangeValuesArray(unsignedLongType); + for (let axis of validAxisArray) { + let size = dimensions2D[axis]; + const mean = builder.input(`mean${++meanIndex}`, {dataType, dimensions: [size]}); + const variance = builder.input(`variance${++varianceIndex}`, {dataType, dimensions: [size]}); + for (let invalidAxis of invalidAxisArray) { + assert_throws_js(TypeError, () => builder.batchNormalization(input, mean, variance, {axis: invalidAxis})); + } + } + } +}, "[batchNormalization] TypeError is expected if options.axis is outside the 'unsigned long' value range"); + +promise_test(async t => { + for (let dataType of allWebNNOperandDataTypes) { + const input = builder.input(`input${++inputIndex}`, {dataType, dimensions: dimensions2D}); + const validAxisArray = getAxisArray(dimensions2D); + for (let axis of validAxisArray) { + let size = dimensions2D[axis]; + const mean = builder.input(`mean${++meanIndex}`, {dataType, dimensions: [size]}); + const variance = builder.input(`variance${++varianceIndex}`, {dataType, dimensions: [size]}); + assert_throws_dom('DataError', () => builder.batchNormalization(input, mean, variance, {axis: getRank(dimensions2D)})); + } + } +}, "[batchNormalization] DataError is expected if options.axis is 'unsigned long' and it's not in the range 0 to the rank of input, exclusive"); + +promise_test(async t => { + for (let dataType of allWebNNOperandDataTypes) { + const input = builder.input(`input${++inputIndex}`, {dataType, dimensions: dimensions2D}); + const validAxisArray = getAxisArray(dimensions2D); + for (let axis of validAxisArray) { + let size = dimensions2D[axis]; + const mean = builder.input(`mean${++meanIndex}`, {dataType, dimensions: [size]}); + const variance = builder.input(`variance${++varianceIndex}`, {dataType, dimensions: [size]}); + for (let axis of notUnsignedLongAxisArray) { + assert_false(typeof axis === 'number' && Number.isInteger(axis), "[batchNormalization] options.axis should be of 'unsigned long'"); + assert_throws_js(TypeError, () => builder.batchNormalization(input, mean, variance, {axis})); + } + } + } +}, '[batchNormalization] TypeError is expected if options.axis is not an unsigned long interger'); + +promise_test(async t => { + for (let dataType of allWebNNOperandDataTypes) { + const input = builder.input(`input${++inputIndex}`, {dataType, dimensions: dimensions2D}); + const validAxisArray = getAxisArray(dimensions2D); + for (let axis of validAxisArray) { + const variance = builder.input(`variance${++varianceIndex}`, {dataType, dimensions: [dimensions2D[axis]]}); + for (let dimensions of allWebNNDimensionsArray) { + if (dimensions.length !== 1) { + // set mean not be 1D tensor + const mean = builder.input(`mean${++meanIndex}`, {dataType, dimensions}); + assert_throws_dom('DataError', () => builder.batchNormalization(input, mean, variance)); + } + } + } + } +}, "[batchNormalization] DataError is expected if the size of mean.dimensions is not 1"); + +promise_test(async t => { + for (let dataType of allWebNNOperandDataTypes) { + const input = builder.input(`input${++inputIndex}`, {dataType, dimensions: dimensions2D}); + const validAxisArray = getAxisArray(dimensions2D); + for (let axis of validAxisArray) { + let size = dimensions2D[axis]; + const variance = builder.input(`variance${++varianceIndex}`, {dataType, dimensions: [size]}); + for (let offset of adjustOffsetsArray) { + const adjustedSize = size + offset; + const mean = builder.input(`mean${++meanIndex}`, {dataType, dimensions: [adjustedSize]}); + assert_throws_dom('DataError', () => builder.batchNormalization(input, mean, variance, {axis})); + } + } + } +}, "[batchNormalization] DataError is expected if mean.dimensions[0] is not equal to input.dimensions[options.axis]"); + +promise_test(async t => { + for (let dataType of allWebNNOperandDataTypes) { + const input = builder.input(`input${++inputIndex}`, {dataType, dimensions: dimensions2D}); + const validAxisArray = getAxisArray(dimensions2D); + for (let axis of validAxisArray) { + const mean = builder.input(`mean${++meanIndex}`, {dataType, dimensions: [dimensions2D[axis]]}); + for (let dimensions of allWebNNDimensionsArray) { + if (dimensions.length !== 1) { + // set variance not be 1D tensor + const variance = builder.input(`variance${++varianceIndex}`, {dataType, dimensions}); + assert_throws_dom('DataError', () => builder.batchNormalization(input, mean, variance)); + } + } + } + } +}, "[batchNormalization] DataError is expected if the size of variance.dimensions is not 1"); + +promise_test(async t => { + for (let dataType of allWebNNOperandDataTypes) { + const input = builder.input(`input${++inputIndex}`, {dataType, dimensions: dimensions2D}); + const validAxisArray = getAxisArray(dimensions2D); + for (let axis of validAxisArray) { + let size = dimensions2D[axis]; + const mean = builder.input(`mean${++meanIndex}`, {dataType, dimensions: [size]}); + for (let offset of adjustOffsetsArray) { + const adjustedSize = size + offset; + const variance = builder.input(`variance${++varianceIndex}`, {dataType, dimensions: [adjustedSize]}); + assert_throws_dom('DataError', () => builder.batchNormalization(input, mean, variance, {axis})); + } + } + } +}, "[batchNormalization] DataError is expected if variance.dimensions[0] is not equal to input.dimensions[options.axis]"); + +promise_test(async t => { + for (let dataType of allWebNNOperandDataTypes) { + const input = builder.input(`input${++inputIndex}`, {dataType, dimensions: dimensions2D}); + const validAxisArray = getAxisArray(dimensions2D); + for (let axis of validAxisArray) { + const mean = builder.input(`mean${++meanIndex}`, {dataType, dimensions: [dimensions2D[axis]]}); + const variance = builder.input(`variance${++varianceIndex}`, {dataType, dimensions: [dimensions2D[axis]]}); + for (let dimensions of allWebNNDimensionsArray) { + if (dimensions.length !== 1) { + // set scale not be 1D tensor + const scale = builder.input('scale', {dataType, dimensions}); + assert_throws_dom('DataError', () => builder.batchNormalization(input, mean, variance, {axis, scale})); + } + } + } + } +}, "[batchNormalization] DataError is expected if the size of scale.dimensions is not 1"); + +promise_test(async t => { + for (let dataType of allWebNNOperandDataTypes) { + const input = builder.input(`input${++inputIndex}`, {dataType, dimensions: dimensions2D}); + const validAxisArray = getAxisArray(dimensions2D); + for (let axis of validAxisArray) { + let size = dimensions2D[axis]; + const mean = builder.input(`mean${++meanIndex}`, {dataType, dimensions: [size]}); + const variance = builder.input(`variance${++varianceIndex}`, {dataType, dimensions: [size]}); + for (let offset of adjustOffsetsArray) { + const adjustedSize = size + offset; + const scale = builder.input('scale', {dataType, dimensions: [adjustedSize]}); + assert_throws_dom('DataError', () => builder.batchNormalization(input, mean, variance, {axis, scale})); + } + } + } +}, "[batchNormalization] DataError is expected if scale.dimensions[0] is not equal to input.dimensions[options.axis]"); + +promise_test(async t => { + for (let dataType of allWebNNOperandDataTypes) { + const input = builder.input(`input${++inputIndex}`, {dataType, dimensions: dimensions2D}); + const validAxisArray = getAxisArray(dimensions2D); + for (let axis of validAxisArray) { + const mean = builder.input(`mean${++meanIndex}`, {dataType, dimensions: [dimensions2D[axis]]}); + const variance = builder.input(`variance${++varianceIndex}`, {dataType, dimensions: [dimensions2D[axis]]}); + for (let dimensions of allWebNNDimensionsArray) { + if (dimensions.length !== 1) { + // set bias not be 1D tensor + const bias = builder.input('bias', {dataType, dimensions}); + assert_throws_dom('DataError', () => builder.batchNormalization(input, mean, variance, {axis, bias})); + } + } + } + } +}, "[batchNormalization] DataError is expected if the size of bias.dimensions is not 1"); + +promise_test(async t => { + for (let dataType of allWebNNOperandDataTypes) { + const input = builder.input(`input${++inputIndex}`, {dataType, dimensions: dimensions2D}); + const validAxisArray = getAxisArray(dimensions2D); + for (let axis of validAxisArray) { + let size = dimensions2D[axis]; + const mean = builder.input(`mean${++meanIndex}`, {dataType, dimensions: [size]}); + const variance = builder.input(`variance${++varianceIndex}`, {dataType, dimensions: [size]}); + for (let offset of adjustOffsetsArray) { + const adjustedSize = size + offset; + const bias = builder.input('bias', {dataType, dimensions: [adjustedSize]}); + assert_throws_dom('DataError', () => builder.batchNormalization(input, mean, variance, {axis, bias})); + } + } + } +}, "[batchNormalization] DataError is expected if bias.dimensions[0] is not equal to input.dimensions[options.axis]"); diff --git a/testing/web-platform/tests/webnn/validation_tests/elementwise_binary.https.any.js b/testing/web-platform/tests/webnn/validation_tests/elementwise_binary.https.any.js new file mode 100644 index 0000000000..97a1a2b93c --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/elementwise_binary.https.any.js @@ -0,0 +1,11 @@ +// META: title=validation tests for WebNN API element-wise binary operations +// META: global=window,dedicatedworker +// META: script=../resources/utils_validation.js +// META: timeout=long + +'use strict'; + +['add', 'sub', 'mul', 'div', 'max', 'min', 'pow'].forEach((operationName) => { + validateTwoInputsOfSameDataType(operationName); + validateTwoInputsBroadcastable(operationName); +}); diff --git a/testing/web-platform/tests/webnn/validation_tests/gather.https.any.js b/testing/web-platform/tests/webnn/validation_tests/gather.https.any.js new file mode 100644 index 0000000000..67ac7d7be3 --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/gather.https.any.js @@ -0,0 +1,62 @@ +// META: title=validation tests for WebNN API gather operation +// META: global=window,dedicatedworker +// META: script=../resources/utils_validation.js +// META: timeout=long + +'use strict'; + +const tests = [ + { + name: '[gather] Test gather with default options and 0-D indices', + input: {dataType: 'int32', dimensions: [3]}, + indices: {dataType: 'uint64', dimensions: []}, + output: {dataType: 'int32', dimensions: []} + }, + { + name: '[gather] Test gather with axis = 2', + input: {dataType: 'float32', dimensions: [1, 2, 3, 4]}, + indices: {dataType: 'int64', dimensions: [5, 6]}, + axis: 2, + output: {dataType: 'float32', dimensions: [1, 2, 5, 6, 4]} + }, + { + name: '[gather] TypeError is expected if the input is a scalar', + input: {dataType: 'float16', dimensions: []}, + indices: {dataType: 'int64', dimensions: [1]} + }, + { + name: '[gather] TypeError is expected if the axis is greater than the rank of input', + input: {dataType: 'float16', dimensions: [1, 2, 3]}, + indices: {dataType: 'int32', dimensions: [5, 6]}, + axis: 4 + }, + { + name: '[gather] TypeError is expected if the data type of indices is invalid', + input: {dataType: 'float16', dimensions: [1, 2, 3, 4]}, + indices: {dataType: 'float32', dimensions: [5, 6]} + } +]; + +tests.forEach( + test => promise_test(async t => { + const input = builder.input( + 'input', + {dataType: test.input.dataType, dimensions: test.input.dimensions}); + const indices = builder.input( + 'indices', + {dataType: test.indices.dataType, dimensions: test.indices.dimensions}); + + const options = {}; + if (test.axis) { + options.axis = test.axis; + } + + if (test.output) { + const output = builder.gather(input, indices, options); + assert_equals(output.dataType(), test.output.dataType); + assert_array_equals(output.shape(), test.output.dimensions); + } else { + assert_throws_js( + TypeError, () => builder.gather(input, indices, options)); + } + }, test.name)); diff --git a/testing/web-platform/tests/webnn/validation_tests/gru.https.any.js b/testing/web-platform/tests/webnn/validation_tests/gru.https.any.js new file mode 100644 index 0000000000..295baab9c2 --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/gru.https.any.js @@ -0,0 +1,398 @@ +// META: title=validation tests for WebNN API gru operation +// META: global=window,dedicatedworker +// META: script=../resources/utils_validation.js +// META: timeout=long + +'use strict'; + +const steps = 2, batchSize = 3, inputSize = 4, hiddenSize = 5, + numDirections = 1; + +const tests = [ + { + name: '[gru] Test with default options', + input: { dataType: 'float32', dimensions: [steps, batchSize, inputSize] }, + weight: { + dataType: 'float32', + dimensions: [numDirections, 3 * hiddenSize, inputSize] + }, + recurrentWeight: { + dataType: 'float32', + dimensions: [numDirections, 3 * hiddenSize, hiddenSize] + }, + steps: steps, + hiddenSize: hiddenSize, + outputs: [ + { dataType: 'float32', dimensions: [numDirections, batchSize, hiddenSize] } + ] + }, + { + name: '[gru] Test with given options', + input: { dataType: 'float32', dimensions: [steps, batchSize, inputSize] }, + weight: { + dataType: 'float32', + dimensions: [/*numDirections=*/ 2, 3 * hiddenSize, inputSize] + }, + recurrentWeight: { + dataType: 'float32', + dimensions: [/*numDirections=*/ 2, 3 * hiddenSize, hiddenSize] + }, + steps: steps, + hiddenSize: hiddenSize, + options: { + bias: { + dataType: 'float32', + dimensions: [/*numDirections=*/ 2, 3 * hiddenSize] + }, + recurrentBias: { + dataType: 'float32', + dimensions: [/*numDirections=*/ 2, 3 * hiddenSize] + }, + initialHiddenState: { + dataType: 'float32', + dimensions: [/*numDirections=*/ 2, batchSize, hiddenSize] + }, + restAfter: true, + returnSequence: true, + direction: 'both', + layout: 'rzn', + activations: ['sigmoid', 'relu'] + }, + outputs: [ + { + dataType: 'float32', + dimensions: [/*numDirections=*/ 2, batchSize, hiddenSize] + }, + { + dataType: 'float32', + dimensions: [steps, /*numDirections=*/ 2, batchSize, hiddenSize] + } + ] + }, + { + name: '[gru] TypeError is expected if steps equals to zero', + input: { dataType: 'float32', dimensions: [steps, batchSize, inputSize] }, + weight: { + dataType: 'float32', + dimensions: [numDirections, 4 * hiddenSize, inputSize] + }, + recurrentWeight: { + dataType: 'float32', + dimensions: [numDirections, 4 * hiddenSize, hiddenSize] + }, + steps: 0, + hiddenSize: hiddenSize, + }, + { + name: '[gru] TypeError is expected if hiddenSize equals to zero', + input: { dataType: 'float32', dimensions: [steps, batchSize, inputSize] }, + weight: { + dataType: 'float32', + dimensions: [numDirections, 3 * hiddenSize, inputSize] + }, + recurrentWeight: { + dataType: 'float32', + dimensions: [numDirections, 3 * hiddenSize, hiddenSize] + }, + steps: steps, + hiddenSize: 0 + }, + { + name: '[gru] TypeError is expected if hiddenSize is too large', + input: { dataType: 'float32', dimensions: [steps, batchSize, inputSize] }, + weight: { + dataType: 'float32', + dimensions: [numDirections, 3 * hiddenSize, inputSize] + }, + recurrentWeight: { + dataType: 'float32', + dimensions: [numDirections, 3 * hiddenSize, hiddenSize] + }, + steps: steps, + hiddenSize: 4294967295, + }, + { + name: + '[gru] TypeError is expected if the data type of the inputs is not one of the floating point types', + input: { dataType: 'uint32', dimensions: [steps, batchSize, inputSize] }, + weight: { + dataType: 'uint32', + dimensions: [numDirections, 3 * hiddenSize, inputSize] + }, + recurrentWeight: { + dataType: 'uint32', + dimensions: [numDirections, 3 * hiddenSize, hiddenSize] + }, + steps: steps, + hiddenSize: hiddenSize + }, + { + name: + '[gru] TypeError is expected if the rank of input is not 3', + input: { dataType: 'float32', dimensions: [steps, batchSize] }, + weight: { + dataType: 'float32', + dimensions: [numDirections, 3 * hiddenSize, inputSize] + }, + recurrentWeight: { + dataType: 'float32', + dimensions: [numDirections, 3 * hiddenSize, hiddenSize] + }, + steps: steps, + hiddenSize: hiddenSize + }, + { + name: + '[gru] TypeError is expected if input.dimensions[0] is not equal to steps', + input: { dataType: 'float32', dimensions: [1000, batchSize, inputSize] }, + weight: { + dataType: 'float32', + dimensions: [numDirections, 3 * hiddenSize, inputSize] + }, + recurrentWeight: { + dataType: 'float32', + dimensions: [numDirections, 3 * hiddenSize, hiddenSize] + }, + steps: steps, + hiddenSize: hiddenSize + }, + { + name: '[gru] TypeError is expected if weight.dimensions[1] is not 3 * hiddenSize', + input: { dataType: 'float32', dimensions: [steps, batchSize, inputSize] }, + weight: { + dataType: 'float32', + dimensions: [numDirections, 4 * hiddenSize, inputSize] + }, + recurrentWeight: { + dataType: 'float32', + dimensions: [numDirections, 3 * hiddenSize, hiddenSize] + }, + steps: steps, + hiddenSize: hiddenSize + }, + { + name: + '[gru] TypeError is expected if the rank of recurrentWeight is not 3', + input: { dataType: 'float32', dimensions: [steps, batchSize, inputSize] }, + weight: { + dataType: 'float32', + dimensions: [numDirections, 3 * hiddenSize, inputSize] + }, + recurrentWeight: + { dataType: 'float32', dimensions: [numDirections, 3 * hiddenSize] }, + steps: steps, + hiddenSize: hiddenSize + }, + { + name: + '[gru] TypeError is expected if the recurrentWeight.dimensions is invalid', + input: { dataType: 'float32', dimensions: [steps, batchSize, inputSize] }, + weight: { + dataType: 'float32', + dimensions: [numDirections, 3 * hiddenSize, inputSize] + }, + recurrentWeight: + { dataType: 'float32', dimensions: [numDirections, 4 * hiddenSize, inputSize] }, + steps: steps, + hiddenSize: hiddenSize + }, + { + name: + '[gru] TypeError is expected if the size of options.activations is not 2', + input: { dataType: 'float32', dimensions: [steps, batchSize, inputSize] }, + weight: { + dataType: 'float32', + dimensions: [numDirections, 3 * hiddenSize, inputSize] + }, + recurrentWeight: { + dataType: 'float32', + dimensions: [numDirections, 3 * hiddenSize, hiddenSize] + }, + steps: steps, + hiddenSize: hiddenSize, + options: { activations: ['sigmoid', 'tanh', 'relu'] } + }, + { + name: + '[gru] TypeError is expected if the rank of options.bias is not 2', + input: { dataType: 'float32', dimensions: [steps, batchSize, inputSize] }, + weight: { + dataType: 'float32', + dimensions: [numDirections, 3 * hiddenSize, inputSize] + }, + recurrentWeight: { + dataType: 'float32', + dimensions: [numDirections, 3 * hiddenSize, hiddenSize] + }, + steps: steps, + hiddenSize: hiddenSize, + options: { bias: { dataType: 'float32', dimensions: [numDirections] } } + }, + { + name: + '[gru] TypeError is expected if options.bias.dimensions[1] is not 3 * hiddenSize', + input: { dataType: 'float32', dimensions: [steps, batchSize, inputSize] }, + weight: { + dataType: 'float32', + dimensions: [numDirections, 3 * hiddenSize, inputSize] + }, + recurrentWeight: { + dataType: 'float32', + dimensions: [numDirections, 3 * hiddenSize, hiddenSize] + }, + steps: steps, + hiddenSize: hiddenSize, + options: { bias: { dataType: 'float32', dimensions: [numDirections, hiddenSize] } } + }, + { + name: + '[gru] TypeError is expected if options.recurrentBias.dimensions[1] is not 3 * hiddenSize', + input: { dataType: 'float16', dimensions: [steps, batchSize, inputSize] }, + weight: { + dataType: 'float16', + dimensions: [numDirections, 3 * hiddenSize, inputSize] + }, + recurrentWeight: { + dataType: 'float16', + dimensions: [numDirections, 3 * hiddenSize, hiddenSize] + }, + steps: steps, + hiddenSize: hiddenSize, + options: { + recurrentBias: { dataType: 'float16', dimensions: [numDirections, 4 * hiddenSize] } + } + }, + { + name: + '[gru] TypeError is expected if the rank of options.initialHiddenState is not 3', + input: { dataType: 'float16', dimensions: [steps, batchSize, inputSize] }, + weight: { + dataType: 'float16', + dimensions: [numDirections, 3 * hiddenSize, inputSize] + }, + recurrentWeight: { + dataType: 'float16', + dimensions: [numDirections, 3 * hiddenSize, hiddenSize] + }, + steps: steps, + hiddenSize: hiddenSize, + options: { + initialHiddenState: { + dataType: 'float16', + dimensions: [numDirections, batchSize] + } + } + }, + { + name: + '[gru] TypeError is expected if options.initialHiddenState.dimensions[2] is not inputSize', + input: { dataType: 'float16', dimensions: [steps, batchSize, inputSize] }, + weight: { + dataType: 'float16', + dimensions: [numDirections, 3 * hiddenSize, inputSize] + }, + recurrentWeight: { + dataType: 'float16', + dimensions: [numDirections, 3 * hiddenSize, hiddenSize] + }, + steps: steps, + hiddenSize: hiddenSize, + options: { + initialHiddenState: { + dataType: 'float16', + dimensions: [numDirections, batchSize, 3 * hiddenSize] + } + } + }, + { + name: + '[gru] TypeError is expected if the dataType of options.initialHiddenState is incorrect', + input: { dataType: 'float16', dimensions: [steps, batchSize, inputSize] }, + weight: { + dataType: 'float16', + dimensions: [numDirections, 3 * hiddenSize, inputSize] + }, + recurrentWeight: { + dataType: 'float16', + dimensions: [numDirections, 3 * hiddenSize, hiddenSize] + }, + steps: steps, + hiddenSize: hiddenSize, + options: { + initialHiddenState: { + dataType: 'uint64', + dimensions: [numDirections, batchSize, hiddenSize] + } + } + } +]; + +tests.forEach( + test => promise_test(async t => { + const input = builder.input( + 'input', + { dataType: test.input.dataType, dimensions: test.input.dimensions }); + const weight = builder.input( + 'weight', + { dataType: test.weight.dataType, dimensions: test.weight.dimensions }); + const recurrentWeight = builder.input('recurrentWeight', { + dataType: test.recurrentWeight.dataType, + dimensions: test.recurrentWeight.dimensions + }); + + const options = {}; + if (test.options) { + if (test.options.bias) { + options.bias = builder.input('bias', { + dataType: test.options.bias.dataType, + dimensions: test.options.bias.dimensions + }); + } + if (test.options.recurrentBias) { + options.bias = builder.input('recurrentBias', { + dataType: test.options.recurrentBias.dataType, + dimensions: test.options.recurrentBias.dimensions + }); + } + if (test.options.initialHiddenState) { + options.initialHiddenState = builder.input('initialHiddenState', { + dataType: test.options.initialHiddenState.dataType, + dimensions: test.options.initialHiddenState.dimensions + }); + } + if (test.options.resetAfter) { + options.resetAfter = test.options.resetAfter; + } + if (test.options.returnSequence) { + options.returnSequence = test.options.returnSequence; + } + if (test.options.direction) { + options.direction = test.options.direction; + } + if (test.options.layout) { + options.layout = test.options.layout; + } + if (test.options.activations) { + options.activations = []; + test.options.activations.forEach( + activation => options.activations.push(builder[activation]())); + } + } + + if (test.outputs) { + const outputs = builder.gru( + input, weight, recurrentWeight, test.steps, test.hiddenSize, + 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.gru( + input, weight, recurrentWeight, test.steps, test.hiddenSize, + options)); + } + }, test.name));
\ No newline at end of file diff --git a/testing/web-platform/tests/webnn/validation_tests/layer_normalization.https.any.js b/testing/web-platform/tests/webnn/validation_tests/layer_normalization.https.any.js new file mode 100644 index 0000000000..7dbcf5c74a --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/layer_normalization.https.any.js @@ -0,0 +1,8 @@ +// META: title=validation tests for WebNN API +// META: global=window,dedicatedworker +// META: script=../resources/utils_validation.js +// META: timeout=long + +'use strict'; + +validateOptionsAxes('layerNormalization', 4); diff --git a/testing/web-platform/tests/webnn/validation_tests/lstm.https.any.js b/testing/web-platform/tests/webnn/validation_tests/lstm.https.any.js new file mode 100644 index 0000000000..efa05090ca --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/lstm.https.any.js @@ -0,0 +1,386 @@ +// META: title=validation tests for WebNN API lstm operation +// META: global=window,dedicatedworker +// META: script=../resources/utils_validation.js +// META: timeout=long + +'use strict'; + +const steps = 10, batchSize = 5, inputSize = 3, hiddenSize = 8, + numDirections = 1; + +const tests = [ + { + name: '[lstm] Test with default options', + input: {dataType: 'float16', dimensions: [steps, batchSize, inputSize]}, + weight: { + dataType: 'float16', + dimensions: [numDirections, 4 * hiddenSize, inputSize] + }, + recurrentWeight: { + dataType: 'float16', + dimensions: [numDirections, 4 * hiddenSize, hiddenSize] + }, + steps: steps, + hiddenSize: hiddenSize, + outputs: [ + {dataType: 'float16', dimensions: [numDirections, batchSize, hiddenSize]}, + {dataType: 'float16', dimensions: [numDirections, batchSize, hiddenSize]} + ] + }, + { + name: '[lstm] Test with given options', + input: {dataType: 'float32', dimensions: [steps, batchSize, inputSize]}, + weight: { + dataType: 'float32', + dimensions: [/*numDirections=*/ 2, 4 * hiddenSize, inputSize] + }, + recurrentWeight: { + dataType: 'float32', + dimensions: [/*numDirections=*/ 2, 4 * hiddenSize, hiddenSize] + }, + steps: steps, + hiddenSize: hiddenSize, + options: { + bias: { + dataType: 'float32', + dimensions: [/*numDirections=*/ 2, 4 * hiddenSize] + }, + recurrentBias: { + dataType: 'float32', + dimensions: [/*numDirections=*/ 2, 4 * hiddenSize] + }, + peepholeWeight: { + dataType: 'float32', + dimensions: [/*numDirections=*/ 2, 3 * hiddenSize] + }, + initialHiddenState: { + dataType: 'float32', + dimensions: [/*numDirections=*/ 2, batchSize, hiddenSize] + }, + initialCellState: { + dataType: 'float32', + dimensions: [/*numDirections=*/ 2, batchSize, hiddenSize] + }, + returnSequence: true, + direction: 'both', + layout: 'ifgo', + activations: ['sigmoid', 'relu', 'tanh'] + }, + outputs: [ + { + dataType: 'float32', + dimensions: [/*numDirections=*/ 2, batchSize, hiddenSize] + }, + { + dataType: 'float32', + dimensions: [/*numDirections=*/ 2, batchSize, hiddenSize] + }, + { + dataType: 'float32', + dimensions: [steps, /*numDirections=*/ 2, batchSize, hiddenSize] + } + ] + }, + { + name: '[lstm] DataError is expected if hiddenSize equals to zero', + input: {dataType: 'float32', dimensions: [steps, batchSize, inputSize]}, + weight: { + dataType: 'float32', + dimensions: [numDirections, 4 * hiddenSize, inputSize] + }, + recurrentWeight: { + dataType: 'float32', + dimensions: [numDirections, 4 * hiddenSize, hiddenSize] + }, + steps: steps, + hiddenSize: 0 + }, + { + name: '[lstm] DataError is expected if hiddenSize is too large', + input: {dataType: 'float32', dimensions: [steps, batchSize, inputSize]}, + weight: { + dataType: 'float32', + dimensions: [numDirections, 4 * hiddenSize, inputSize] + }, + recurrentWeight: { + dataType: 'float32', + dimensions: [numDirections, 4 * hiddenSize, hiddenSize] + }, + steps: steps, + hiddenSize: 4294967295, + }, + { + name: '[lstm] DataError is expected if steps equals to zero', + input: {dataType: 'float32', dimensions: [steps, batchSize, inputSize]}, + weight: { + dataType: 'float32', + dimensions: [numDirections, 4 * hiddenSize, inputSize] + }, + recurrentWeight: { + dataType: 'float32', + dimensions: [numDirections, 4 * hiddenSize, hiddenSize] + }, + steps: 0, + hiddenSize: hiddenSize, + }, + { + name: + '[lstm] DataError is expected if the data type is not one of the floating point types', + input: {dataType: 'uint32', dimensions: [steps, batchSize, inputSize]}, + weight: { + dataType: 'uint32', + dimensions: [numDirections, 4 * hiddenSize, inputSize] + }, + recurrentWeight: { + dataType: 'uint32', + dimensions: [numDirections, 4 * hiddenSize, hiddenSize] + }, + steps: steps, + hiddenSize: hiddenSize + }, + { + name: + '[lstm] DataError is expected if the rank of input is not 3', + input: {dataType: 'float32', dimensions: [steps, batchSize]}, + weight: { + dataType: 'float32', + dimensions: [numDirections, 4 * hiddenSize, inputSize] + }, + recurrentWeight: { + dataType: 'float32', + dimensions: [numDirections, 4 * hiddenSize, hiddenSize] + }, + steps: steps, + hiddenSize: hiddenSize + }, + { + name: + '[lstm] DataError is expected if input.dimensions[0] is not equal to steps', + input: {dataType: 'float32', dimensions: [1000, batchSize, inputSize]}, + weight: { + dataType: 'float32', + dimensions: [numDirections, 4 * hiddenSize, inputSize] + }, + recurrentWeight: { + dataType: 'float32', + dimensions: [numDirections, 4 * hiddenSize, hiddenSize] + }, + steps: steps, + hiddenSize: hiddenSize + }, + { + name: '[lstm] DataError is expected if the shape of weight is incorrect', + input: {dataType: 'float32', dimensions: [steps, batchSize, inputSize]}, + weight: { + dataType: 'float32', + dimensions: [numDirections, 4 * hiddenSize, 1000] + }, + recurrentWeight: { + dataType: 'float32', + dimensions: [numDirections, 4 * hiddenSize, hiddenSize] + }, + steps: steps, + hiddenSize: hiddenSize + }, + { + name: + '[lstm] DataError is expected if the rank of recurrentWeight is not 3', + input: {dataType: 'float32', dimensions: [steps, batchSize, inputSize]}, + weight: { + dataType: 'float32', + dimensions: [numDirections, 4 * hiddenSize, inputSize] + }, + recurrentWeight: + {dataType: 'float32', dimensions: [numDirections, 4 * hiddenSize]}, + steps: steps, + hiddenSize: hiddenSize + }, + { + name: + '[lstm] DataError is expected if the size of options.activations is not 3', + input: {dataType: 'float32', dimensions: [steps, batchSize, inputSize]}, + weight: { + dataType: 'float32', + dimensions: [numDirections, 4 * hiddenSize, inputSize] + }, + recurrentWeight: { + dataType: 'float32', + dimensions: [numDirections, 4 * hiddenSize, hiddenSize] + }, + steps: steps, + hiddenSize: hiddenSize, + options: {activations: ['sigmoid', 'tanh']} + }, + { + name: + '[lstm] DataError is expected if the rank of options.bias is not 2', + input: {dataType: 'float16', dimensions: [steps, batchSize, inputSize]}, + weight: { + dataType: 'float16', + dimensions: [numDirections, 4 * hiddenSize, inputSize] + }, + recurrentWeight: { + dataType: 'float16', + dimensions: [numDirections, 4 * hiddenSize, hiddenSize] + }, + steps: steps, + hiddenSize: hiddenSize, + options: {bias: {dataType: 'float16', dimensions: [numDirections]}} + }, + { + name: + '[lstm] DataError is expected if the shape of options.recurrentBias.dimensions is incorrect', + input: {dataType: 'float16', dimensions: [steps, batchSize, inputSize]}, + weight: { + dataType: 'float16', + dimensions: [numDirections, 4 * hiddenSize, inputSize] + }, + recurrentWeight: { + dataType: 'float16', + dimensions: [numDirections, 4 * hiddenSize, hiddenSize] + }, + steps: steps, + hiddenSize: hiddenSize, + options: { + recurrentBias: {dataType: 'float16', dimensions: [numDirections, 1000]} + } + }, + { + name: + '[lstm] DataError is expected if the dataType of options.peepholeWeight is incorrect', + input: {dataType: 'float16', dimensions: [steps, batchSize, inputSize]}, + weight: { + dataType: 'float16', + dimensions: [numDirections, 4 * hiddenSize, inputSize] + }, + recurrentWeight: { + dataType: 'float16', + dimensions: [numDirections, 4 * hiddenSize, hiddenSize] + }, + steps: steps, + hiddenSize: hiddenSize, + options: { + peepholeWeight: + {dataType: 'float32', dimensions: [numDirections, 3 * hiddenSize]} + } + }, + { + name: + '[lstm] DataError is expected if the dataType of options.initialHiddenState is incorrect', + input: {dataType: 'float16', dimensions: [steps, batchSize, inputSize]}, + weight: { + dataType: 'float16', + dimensions: [numDirections, 4 * hiddenSize, inputSize] + }, + recurrentWeight: { + dataType: 'float16', + dimensions: [numDirections, 4 * hiddenSize, hiddenSize] + }, + steps: steps, + hiddenSize: hiddenSize, + options: { + initialHiddenState: { + dataType: 'uint64', + dimensions: [numDirections, batchSize, hiddenSize] + } + } + }, + { + name: + '[lstm] DataError is expected if the shape of options.initialCellState is incorrect', + input: {dataType: 'float32', dimensions: [steps, batchSize, inputSize]}, + weight: { + dataType: 'float32', + dimensions: [numDirections, 4 * hiddenSize, inputSize] + }, + recurrentWeight: { + dataType: 'float32', + dimensions: [numDirections, 4 * hiddenSize, hiddenSize] + }, + steps: steps, + hiddenSize: hiddenSize, + options: { + initialCellState: + {dataType: 'float32', dimensions: [numDirections, batchSize, 1000]} + } + } +]; + +tests.forEach( + test => promise_test(async t => { + const input = builder.input( + 'input', + {dataType: test.input.dataType, dimensions: test.input.dimensions}); + const weight = builder.input( + 'weight', + {dataType: test.weight.dataType, dimensions: test.weight.dimensions}); + const recurrentWeight = builder.input('recurrentWeight', { + dataType: test.recurrentWeight.dataType, + dimensions: test.recurrentWeight.dimensions + }); + + const options = {}; + if (test.options) { + if (test.options.bias) { + options.bias = builder.input('bias', { + dataType: test.options.bias.dataType, + dimensions: test.options.bias.dimensions + }); + } + if (test.options.recurrentBias) { + options.bias = builder.input('recurrentBias', { + dataType: test.options.recurrentBias.dataType, + dimensions: test.options.recurrentBias.dimensions + }); + } + if (test.options.peepholeWeight) { + options.peepholeWeight = builder.input('peepholeWeight', { + dataType: test.options.peepholeWeight.dataType, + dimensions: test.options.peepholeWeight.dimensions + }); + } + if (test.options.initialHiddenState) { + options.initialHiddenState = builder.input('initialHiddenState', { + dataType: test.options.initialHiddenState.dataType, + dimensions: test.options.initialHiddenState.dimensions + }); + } + if (test.options.initialCellState) { + options.initialCellState = builder.input('initialCellState', { + dataType: test.options.initialCellState.dataType, + dimensions: test.options.initialCellState.dimensions + }); + } + if (test.options.returnSequence) { + options.returnSequence = test.options.returnSequence; + } + if (test.options.direction) { + options.direction = test.options.direction; + } + if (test.options.layout) { + options.layout = test.options.layout; + } + if (test.options.activations) { + options.activations = []; + test.options.activations.forEach( + activation => options.activations.push(builder[activation]())); + } + } + + if (test.outputs) { + const outputs = builder.lstm( + input, weight, recurrentWeight, test.steps, test.hiddenSize, + 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_dom( + 'DataError', + () => builder.lstm( + input, weight, recurrentWeight, test.steps, test.hiddenSize, + options)); + } + }, test.name)); diff --git a/testing/web-platform/tests/webnn/validation_tests/reduction.https.any.js b/testing/web-platform/tests/webnn/validation_tests/reduction.https.any.js new file mode 100644 index 0000000000..65b71239b9 --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/reduction.https.any.js @@ -0,0 +1,21 @@ +// META: title=validation tests for WebNN API reduction operation +// META: global=window,dedicatedworker +// META: script=../resources/utils_validation.js +// META: timeout=long + +'use strict'; + +[ + 'reduceL1', + 'reduceL2', + 'reduceLogSum', + 'reduceLogSumExp', + 'reduceMax', + 'reduceMean', + 'reduceMin', + 'reduceProduct', + 'reduceSum', + 'reduceSumSquare', +].forEach((operationName) => { + validateOptionsAxes(operationName); +}); diff --git a/testing/web-platform/tests/webnn/validation_tests/resample2d.https.any.js b/testing/web-platform/tests/webnn/validation_tests/resample2d.https.any.js new file mode 100644 index 0000000000..2e00cf297c --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/resample2d.https.any.js @@ -0,0 +1,8 @@ +// META: title=validation tests for WebNN API resample2d operation +// META: global=window,dedicatedworker +// META: script=../resources/utils_validation.js +// META: timeout=long + +'use strict'; + +validateOptionsAxes('resample2d', 4); diff --git a/testing/web-platform/tests/webnn/validation_tests/triangular.https.any.js b/testing/web-platform/tests/webnn/validation_tests/triangular.https.any.js new file mode 100644 index 0000000000..4e4c368f82 --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/triangular.https.any.js @@ -0,0 +1,16 @@ +// META: title=validation tests for WebNN API triangular operation +// META: global=window,dedicatedworker +// META: script=../resources/utils_validation.js +// META: timeout=long + +'use strict'; + +promise_test(async t => { + // The input tensor which is at least 2-D. + for (let dimensions of allWebNNDimensionsArray.slice(0, 2)) { + for (let dataType of allWebNNOperandDataTypes) { + const input = builder.input(`input${++inputIndex}`, {dataType, dimensions}); + assert_throws_js(TypeError, () => builder.triangular(input)); + } + } +}, "[triangular] DataError is expected if input's rank is less than 2"); |