summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/webnn/validation_tests
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/webnn/validation_tests')
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/arg_min_max.https.any.js8
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/batch_normalization.https.any.js190
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/elementwise_binary.https.any.js11
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/gather.https.any.js62
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/gru.https.any.js398
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/layer_normalization.https.any.js8
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/lstm.https.any.js386
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/reduction.https.any.js21
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/resample2d.https.any.js8
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/triangular.https.any.js16
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");