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/argMinMax.https.any.js15
-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/batchNormalization.https.any.js (renamed from testing/web-platform/tests/webnn/validation_tests/batch_normalization.https.any.js)82
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/cast.https.any.js13
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/clamp.https.any.js7
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/compute-multiple-arraybufferviews-sharing-same-arraybuffer.https.any.js50
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/concat.https.any.js106
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/constant.https.any.js141
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/conv2d.https.any.js57
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/convTranspose2d.https.any.js59
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/elementwise-binary.https.any.js21
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/elementwise-logical.https.any copy.js20
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/elementwise-unary.https.any.js39
-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/elu.https.any.js7
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/expand.https.any.js14
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/gather.https.any.js21
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/gemm.https.any.js21
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/gru.https.any.js795
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/gruCell.https.any.js471
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/hardSigmoid.https.any.js7
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/hardSwish.https.any.js10
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/input.https.any.js70
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/instanceNormalization.https.any.js43
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/layerNormalization.https.any.js32
-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/leakyRelu.https.any.js7
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/linear.https.any.js7
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/lstm.https.any.js347
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/lstmCell.https.any.js600
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/matmul.https.any.js7
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/pad.https.any.js17
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/pooling.https.any.js275
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/prelu.https.any.js7
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/reduction.https.any.js12
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/relu.https.any.js10
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/resample2d.https.any.js154
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/reshape.https.any.js14
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/sigmoid.https.any.js10
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/slice.https.any.js15
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/softmax.https.any.js7
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/softplus.https.any.js7
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/softsign.https.any.js10
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/split.https.any.js14
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/tanh.https.any.js10
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/transpose.https.any.js7
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/triangular.https.any.js3
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/where.https.any.js129
48 files changed, 3255 insertions, 542 deletions
diff --git a/testing/web-platform/tests/webnn/validation_tests/argMinMax.https.any.js b/testing/web-platform/tests/webnn/validation_tests/argMinMax.https.any.js
new file mode 100644
index 0000000000..6e8d558306
--- /dev/null
+++ b/testing/web-platform/tests/webnn/validation_tests/argMinMax.https.any.js
@@ -0,0 +1,15 @@
+// META: title=validation tests for WebNN API argMin/Max operations
+// META: global=window,dedicatedworker
+// META: script=../resources/utils_validation.js
+
+'use strict';
+
+const kArgMinMaxOperators = [
+ 'argMin',
+ 'argMax',
+];
+
+kArgMinMaxOperators.forEach((operatorName) => {
+ validateOptionsAxes(operatorName);
+ validateInputFromAnotherBuilder(operatorName);
+});
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
deleted file mode 100644
index 700be83b04..0000000000
--- a/testing/web-platform/tests/webnn/validation_tests/arg_min_max.https.any.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// 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/batchNormalization.https.any.js
index 25b542d34e..cafd9f9cd5 100644
--- a/testing/web-platform/tests/webnn/validation_tests/batch_normalization.https.any.js
+++ b/testing/web-platform/tests/webnn/validation_tests/batchNormalization.https.any.js
@@ -1,13 +1,23 @@
// 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;
+const kExampleInputDescriptor = {
+ dataType: 'float32',
+ dimensions: [2, 2]
+};
+// 1D tensor descriptor which may be used for `mean`, `variance`, `scale`, or
+// `bias` inputs.
+const kExample1DTensorDescriptor = {
+ dataType: 'float32',
+ dimensions: [kExampleInputDescriptor.dimensions[/* axis */ 1]]
+};
+
promise_test(async t => {
for (let dataType of allWebNNOperandDataTypes) {
const input = builder.input(`input${++inputIndex}`, {dataType, dimensions: dimensions2D});
@@ -188,3 +198,73 @@ promise_test(async t => {
}
}
}, "[batchNormalization] DataError is expected if bias.dimensions[0] is not equal to input.dimensions[options.axis]");
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const inputFromOtherBuilder =
+ otherBuilder.input('input', kExampleInputDescriptor);
+
+ const mean = builder.input('mean', kExample1DTensorDescriptor);
+ const variance = builder.input('variance', kExample1DTensorDescriptor);
+ assert_throws_js(
+ TypeError, () => builder.batchNormalization(inputFromOtherBuilder, mean, variance));
+}, '[batchNormalization] throw if input is from another builder');
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const meanFromOtherBuilder =
+ otherBuilder.input('mean', kExample1DTensorDescriptor);
+
+ const input = builder.input('input', kExampleInputDescriptor);
+ const variance = builder.input('variance', kExample1DTensorDescriptor);
+ assert_throws_js(
+ TypeError,
+ () => builder.batchNormalization(input, meanFromOtherBuilder, variance));
+}, '[batchNormalization] throw if mean is from another builder');
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const varianceFromOtherBuilder =
+ otherBuilder.input('variance', kExample1DTensorDescriptor);
+
+ const input = builder.input('input', kExampleInputDescriptor);
+ const mean = builder.input('mean', kExample1DTensorDescriptor);
+ assert_throws_js(
+ TypeError,
+ () => builder.batchNormalization(input, mean, varianceFromOtherBuilder));
+}, '[batchNormalization] throw if variance is from another builder');
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const scaleFromOtherBuilder =
+ otherBuilder.input('scale', kExample1DTensorDescriptor);
+ const options = {scale: scaleFromOtherBuilder};
+
+ const input = builder.input('input', kExampleInputDescriptor);
+ const mean = builder.input('mean', kExample1DTensorDescriptor);
+ const variance = builder.input('variance', kExample1DTensorDescriptor);
+ assert_throws_js(
+ TypeError,
+ () => builder.batchNormalization(input, mean, variance, options));
+}, '[batchNormalization] throw if scale option is from another builder');
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const biasFromOtherBuilder =
+ otherBuilder.input('bias', kExample1DTensorDescriptor);
+ const options = {scale: biasFromOtherBuilder};
+
+ const input = builder.input('input', kExampleInputDescriptor);
+ const mean = builder.input('mean', kExample1DTensorDescriptor);
+ const variance = builder.input('variance', kExample1DTensorDescriptor);
+ assert_throws_js(
+ TypeError,
+ () => builder.batchNormalization(input, mean, variance, options));
+}, '[batchNormalization] throw if bias option is from another builder');
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const activationFromOtherBuilder = otherBuilder.clamp();
+ const options = {activation: activationFromOtherBuilder};
+
+ const input = builder.input('input', kExampleInputDescriptor);
+ const mean = builder.input('mean', kExample1DTensorDescriptor);
+ const variance = builder.input('variance', kExample1DTensorDescriptor);
+ assert_throws_js(
+ TypeError,
+ () => builder.batchNormalization(input, mean, variance, options));
+}, '[batchNormalization] throw if activation option is from another builder');
diff --git a/testing/web-platform/tests/webnn/validation_tests/cast.https.any.js b/testing/web-platform/tests/webnn/validation_tests/cast.https.any.js
new file mode 100644
index 0000000000..f616203a88
--- /dev/null
+++ b/testing/web-platform/tests/webnn/validation_tests/cast.https.any.js
@@ -0,0 +1,13 @@
+// META: title=validation tests for WebNN API cast operation
+// META: global=window,dedicatedworker
+// META: script=../resources/utils_validation.js
+
+'use strict';
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const inputFromOtherBuilder =
+ otherBuilder.input('input', {dataType: 'int32', dimensions: [2, 2]});
+
+ assert_throws_js(
+ TypeError, () => builder.cast(inputFromOtherBuilder, 'int64'));
+}, '[cast] throw if input is from another builder');
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
new file mode 100644
index 0000000000..85cd19a566
--- /dev/null
+++ b/testing/web-platform/tests/webnn/validation_tests/clamp.https.any.js
@@ -0,0 +1,7 @@
+// META: title=validation tests for WebNN API clamp operation
+// META: global=window,dedicatedworker
+// META: script=../resources/utils_validation.js
+
+'use strict';
+
+validateInputFromAnotherBuilder('clamp');
diff --git a/testing/web-platform/tests/webnn/validation_tests/compute-multiple-arraybufferviews-sharing-same-arraybuffer.https.any.js b/testing/web-platform/tests/webnn/validation_tests/compute-multiple-arraybufferviews-sharing-same-arraybuffer.https.any.js
new file mode 100644
index 0000000000..42b123a97e
--- /dev/null
+++ b/testing/web-platform/tests/webnn/validation_tests/compute-multiple-arraybufferviews-sharing-same-arraybuffer.https.any.js
@@ -0,0 +1,50 @@
+// META: title=ensure WebNN MLContext.compute() rejecting detached buffers
+// META: global=window,dedicatedworker
+// META: script=../resources/utils_validation.js
+
+// These tests are used to reproduce the Chromium issue:
+// https://issues.chromium.org/issues/332002364
+promise_test(async t => {
+ const a = builder.input('a', {dataType: 'float32', dimensions: [2]});
+ const b = builder.input('b', {dataType: 'float32', dimensions: [2]});
+ const c = builder.add(a, b);
+ const graph = await builder.build({c});
+ const arraybuffer = new ArrayBuffer(100);
+ const aBuffer = new Float32Array(arraybuffer, 0, 2);
+ const bBuffer = new Float32Array(arraybuffer, 8, 2);
+ const cBuffer = new Float32Array(2);
+ const promise = context.compute(graph, {'a': aBuffer, 'b': bBuffer}, {'c': cBuffer});
+ promise_rejects_js(t, TypeError, promise);
+ }, 'Throw if two input ArrayBufferViews sharing the same ArrayBuffer');
+
+promise_test(async t => {
+ const a = builder.input('a', {dataType: 'float32', dimensions: [2]});
+ const [b, c] = builder.split(a, 2);
+ const graph = await builder.build({b, c});
+ const aBuffer = new Float32Array(2);
+ const arraybuffer = new ArrayBuffer(100);
+ const bBuffer = new Float32Array(arraybuffer, 0, 1);
+ const cBuffer = new Float32Array(arraybuffer, 4, 1);
+ const promise = context.compute(graph, {'a': aBuffer}, {'b': bBuffer, 'c': cBuffer});
+ promise_rejects_js(t, TypeError, promise);
+}, 'Throw if two output ArrayBufferViews sharing the same ArrayBuffer');
+
+promise_test(async t => {
+ const a = builder.input('a', {dataType: 'float32', dimensions: [2]});
+ const b = builder.relu(a);
+ const graph = await builder.build({b});
+ const arraybuffer = new ArrayBuffer(100);
+ const aBuffer = new Float32Array(arraybuffer, 0, 2);
+ const bBuffer = new Float32Array(arraybuffer, 8, 2);
+ const promise = context.compute(graph, {'a': aBuffer}, {'b': bBuffer});
+ promise_rejects_js(t, TypeError, promise);
+}, 'Throw if input and output ArrayBufferViews sharing the same ArrayBuffer');
+
+promise_test(async t => {
+ const a = builder.input('a', {dataType: 'float32', dimensions: [2]});
+ const b = builder.relu(a);
+ const graph = await builder.build({b});
+ const buffer = new Float32Array(2);
+ const promise = context.compute(graph, {'a': buffer}, {'b': buffer});
+ promise_rejects_js(t, TypeError, promise);
+}, 'Throw if input and output are the same ArrayBufferView');
diff --git a/testing/web-platform/tests/webnn/validation_tests/concat.https.any.js b/testing/web-platform/tests/webnn/validation_tests/concat.https.any.js
new file mode 100644
index 0000000000..b61f2d2bc7
--- /dev/null
+++ b/testing/web-platform/tests/webnn/validation_tests/concat.https.any.js
@@ -0,0 +1,106 @@
+// META: title=validation tests for WebNN API concat operation
+// META: global=window,dedicatedworker
+// META: script=../resources/utils_validation.js
+
+'use strict';
+
+const tests = [
+ {
+ name: '[concat] Test building Concat with one input.',
+ inputs: [{dataType: 'float32', dimensions: [4,4,3]}],
+ axis: 2,
+ output: {dataType: 'float32', dimensions: [4,4,3]}
+ },
+ {
+ name: '[concat] Test building Concat with two inputs',
+ inputs: [{dataType: 'float32', dimensions: [3,1,5]},
+ {dataType: 'float32', dimensions: [3,2,5]}],
+ axis: 1,
+ output: {dataType: 'float32', dimensions: [3,3,5]}
+ },
+ {
+ name: '[concat] Test building Concat with three inputs',
+ inputs: [{dataType: 'float32', dimensions: [3,5,1]},
+ {dataType: 'float32', dimensions: [3,5,2]},
+ {dataType: 'float32', dimensions: [3,5,3]}],
+ axis: 2,
+ output: {dataType: 'float32', dimensions: [3,5,6]}
+ },
+ {
+ name: '[concat] Test building Concat with two 1D inputs.',
+ inputs: [{dataType: 'float32', dimensions: [1]},
+ {dataType: 'float32', dimensions: [1]}],
+ axis: 0,
+ output: {dataType: 'float32', dimensions: [2]}
+ },
+ {
+ name: '[concat] Throw if the inputs are empty.',
+ axis: 0,
+ },
+ {
+ name: '[concat] Throw if the argument types are inconsistent.',
+ inputs: [{dataType: 'float32', dimensions: [1,1]},
+ {dataType: 'int32', dimensions: [1,1]}],
+ axis: 0,
+ },
+ {
+ name: '[concat] Throw if the inputs have different ranks.',
+ inputs: [{dataType: 'float32', dimensions: [1,1]},
+ {dataType: 'float32', dimensions: [1,1,1]}],
+ axis: 0,
+ },
+ {
+ name: '[concat] Throw if the axis is equal to or greater than the size of ranks',
+ inputs: [{dataType: 'float32', dimensions: [1,1]},
+ {dataType: 'float32', dimensions: [1,1]}],
+ axis: 2,
+ },
+ {
+ name: '[concat] Throw if concat with two 0-D scalars.',
+ inputs: [{dataType: 'float32', dimensions: []},
+ {dataType: 'float32', dimensions: []}],
+ axis: 0,
+ },
+ {
+ name: '[concat] Throw if the inputs have other axes with different sizes except on the axis.',
+ inputs: [{dataType: 'float32', dimensions: [1,1,1]},
+ {dataType: 'float32', dimensions: [1,2,3]}],
+ axis: 1,
+ },
+
+];
+
+tests.forEach(test =>
+ promise_test(async t => {
+ let inputs = [];
+ if (test.inputs) {
+ for (let i = 0; i < test.inputs.length; ++i) {
+ inputs[i] = builder.input(
+ `inputs[${i}]`,
+ { dataType: test.inputs[i].dataType, dimensions: test.inputs[i].dimensions }
+ );
+ }
+ }
+ if (test.output) {
+ const output = builder.concat(inputs, test.axis);
+ assert_equals(output.dataType(), test.output.dataType);
+ assert_array_equals(output.shape(), test.output.dimensions);
+ } else {
+ assert_throws_js(TypeError, () => builder.concat(inputs, test.axis));
+ }
+ }, test.name)
+ );
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const operandDescriptor = {dataType: 'float32', dimensions: [2, 2]};
+
+ const inputFromOtherBuilder = otherBuilder.input('input', operandDescriptor);
+
+ const input1 = builder.input('input', operandDescriptor);
+ const input2 = builder.input('input', operandDescriptor);
+ const input3 = builder.input('input', operandDescriptor);
+
+ assert_throws_js(
+ TypeError,
+ () => builder.concat([input1, input2, inputFromOtherBuilder, input3]));
+}, '[concat] throw if any input is from another builder');
diff --git a/testing/web-platform/tests/webnn/validation_tests/constant.https.any.js b/testing/web-platform/tests/webnn/validation_tests/constant.https.any.js
new file mode 100644
index 0000000000..86a60ee209
--- /dev/null
+++ b/testing/web-platform/tests/webnn/validation_tests/constant.https.any.js
@@ -0,0 +1,141 @@
+// META: title=validation tests for WebNN API constant interface
+// META: global=window,dedicatedworker
+// META: script=../resources/utils_validation.js
+
+'use strict';
+
+const tests = [
+ // Tests for constant(descriptor, bufferView)
+ {
+ name:
+ '[constant] Test building a 0-D scalar constant without presenting dimensions',
+ descriptor: {dataType: 'float32'},
+ bufferView: {type: Float32Array, byteLength: 1 * 4},
+ output: {dataType: 'float32', dimensions: []}
+ },
+ {
+ name:
+ '[constant] Test building a 0-D scalar constant with empty dimensions',
+ descriptor: {dataType: 'float32', dimensions: []},
+ bufferView: {type: Float32Array, byteLength: 1 * 4},
+ output: {dataType: 'float32', dimensions: []}
+ },
+ {
+ name: '[constant] Test building a constant with float32 data type',
+ descriptor: {dataType: 'float32', dimensions: [2, 3]},
+ bufferView: {type: Float32Array, byteLength: 6 * 4},
+ output: {dataType: 'float32', dimensions: [2, 3]}
+ },
+ {
+ name:
+ '[constant] Throw if byte length of bufferView for float32 doesn\'t match the given dimensions',
+ descriptor: {dataType: 'float32', dimensions: [2, 3]},
+ bufferView: {
+ type: Float32Array,
+ byteLength: 6 * 4 - 4 // The bufferView's byte length is less than the
+ // one by given dimensions
+ }
+ },
+ // TODO (crbug.com/329702838): Test building a constant with float16 data type
+ {
+ name: '[constant] Test building a constant with int32 data type',
+ descriptor: {dataType: 'int32', dimensions: [2, 3]},
+ bufferView: {type: Int32Array, byteLength: 6 * 4},
+ output: {dataType: 'int32', dimensions: [2, 3]}
+ },
+ {
+ name:
+ '[constant] Throw if byte length of bufferView for int32 doesn\'t match the given dimensions',
+ descriptor: {dataType: 'int32', dimensions: [2, 3]},
+ bufferView: {
+ type: Int32Array,
+ byteLength: 6 * 4 + 4 // The bufferView's byte length is greater than the
+ // one by given dimensions
+ }
+ },
+ {
+ name: '[constant] Test building a constant with uint32 data type',
+ descriptor: {dataType: 'uint32', dimensions: [2, 3]},
+ bufferView: {type: Uint32Array, byteLength: 6 * 4},
+ output: {dataType: 'uint32', dimensions: [2, 3]}
+ },
+ {
+ name:
+ '[constant] Throw if byte length of bufferView for uint32 doesn\'t match the given dimensions',
+ descriptor: {dataType: 'uint32', dimensions: [2, 3]},
+ bufferView: {type: Uint32Array, byteLength: 6 * 4 + 4}
+ },
+ {
+ name: '[constant] Test building a constant with int64 data type',
+ descriptor: {dataType: 'int64', dimensions: [2, 3]},
+ bufferView: {type: BigInt64Array, byteLength: 6 * 8},
+ output: {dataType: 'int64', dimensions: [2, 3]}
+ },
+ {
+ name:
+ '[constant] Throw if byte length of bufferView for int64 doesn\'t match the given dimensions',
+ descriptor: {dataType: 'int64', dimensions: [2, 3]},
+ bufferView: {type: BigInt64Array, byteLength: 6 * 8 + 8}
+ },
+ {
+ name: '[constant] Test building a constant with uint64 data type',
+ descriptor: {dataType: 'uint64', dimensions: [2, 3]},
+ bufferView: {type: BigUint64Array, byteLength: 6 * 8},
+ output: {dataType: 'uint64', dimensions: [2, 3]}
+ },
+ {
+ name:
+ '[constant] Throw if byte length of bufferView for uint64 doesn\'t match the given dimensions',
+ descriptor: {dataType: 'uint64', dimensions: [2, 3]},
+ bufferView: {type: BigUint64Array, byteLength: 6 * 8 + 8}
+ },
+ {
+ name: '[constant] Test building a constant with int8 data type',
+ descriptor: {dataType: 'int8', dimensions: [2, 3]},
+ bufferView: {type: Int8Array, byteLength: 6 * 1},
+ output: {dataType: 'int8', dimensions: [2, 3]}
+ },
+ {
+ name:
+ '[constant] Throw if byte length of bufferView for int8 doesn\'t match the given dimensions',
+ descriptor: {dataType: 'int8', dimensions: [2, 3]},
+ bufferView: {type: Int8Array, byteLength: 6 * 4 - 4}
+ },
+ {
+ name: '[constant] Test building a constant with uint8 data type',
+ descriptor: {dataType: 'uint8', dimensions: [2, 3]},
+ bufferView: {type: Uint8Array, byteLength: 6 * 1},
+ output: {dataType: 'uint8', dimensions: [2, 3]}
+ },
+ {
+ name:
+ '[constant] Throw if byte length of bufferView for uint8 doesn\'t match the given dimensions',
+ descriptor: {dataType: 'uint8', dimensions: [2, 3]},
+ bufferView: {type: Uint8Array, byteLength: 6 * 4 - 4}
+ },
+ {
+ name: '[constant] Throw if a dimension is 0',
+ descriptor: {dataType: 'float32', dimensions: [2, 0]},
+ bufferView: {type: Float32Array, byteLength: 2 * 4}
+ },
+ {
+ name:
+ '[constant] Throw if bufferView type doesn\'t match the operand data type',
+ descriptor: {dataType: 'float32', dimensions: [2, 3]},
+ bufferView: {type: Int32Array, byteLength: 6 * 4}
+ }
+];
+
+tests.forEach(
+ test => promise_test(async t => {
+ const buffer = new ArrayBuffer(test.bufferView.byteLength);
+ const bufferView = new test.bufferView.type(buffer);
+ if (test.output) {
+ const constantOperand = builder.constant(test.descriptor, bufferView);
+ assert_equals(constantOperand.dataType(), test.output.dataType);
+ assert_array_equals(constantOperand.shape(), test.output.dimensions);
+ } else {
+ assert_throws_js(
+ TypeError, () => builder.constant(test.descriptor, bufferView));
+ }
+ }, test.name));
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
new file mode 100644
index 0000000000..ffc9c2c65d
--- /dev/null
+++ b/testing/web-platform/tests/webnn/validation_tests/conv2d.https.any.js
@@ -0,0 +1,57 @@
+// META: title=validation tests for WebNN API conv2d operation
+// META: global=window,dedicatedworker
+// META: script=../resources/utils_validation.js
+
+'use strict';
+
+// Example input in NCHW layout.
+const kExampleInputDescriptor = {
+ dataType: 'float32',
+ dimensions: [1, 1, 5, 5]
+};
+// Example filter in OIHW layout.
+const kExampleFilterDescriptor = {
+ dataType: 'float32',
+ dimensions: [1, 1, 3, 3]
+};
+const kExampleBiasDescriptor = {
+ dataType: 'float32',
+ dimensions: [/* output channels */ 1]
+};
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const inputFromOtherBuilder =
+ otherBuilder.input('input', kExampleInputDescriptor);
+
+ const filter = builder.input('filter', kExampleFilterDescriptor);
+ assert_throws_js(
+ TypeError, () => builder.conv2d(inputFromOtherBuilder, filter));
+}, '[conv2d] throw if input is from another builder');
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const filterFromOtherBuilder =
+ otherBuilder.input('filter', kExampleFilterDescriptor);
+
+ const input = builder.input('input', kExampleInputDescriptor);
+ assert_throws_js(
+ TypeError, () => builder.conv2d(input, filterFromOtherBuilder));
+}, '[conv2d] throw if filter is from another builder');
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const biasFromOtherBuilder =
+ otherBuilder.input('bias', kExampleBiasDescriptor);
+ const options = {inputLayout: 'nchw', bias: biasFromOtherBuilder};
+
+ const input = builder.input('input', kExampleInputDescriptor);
+ const filter = builder.input('filter', kExampleFilterDescriptor);
+ assert_throws_js(TypeError, () => builder.conv2d(input, filter, options));
+}, '[conv2d] throw if bias option is from another builder');
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const activationFromOtherBuilder = otherBuilder.clamp();
+ const options = {activation: activationFromOtherBuilder};
+
+ const input = builder.input('input', kExampleInputDescriptor);
+ const filter = builder.input('filter', kExampleFilterDescriptor);
+ assert_throws_js(TypeError, () => builder.conv2d(input, filter, options));
+}, '[conv2d] throw if activation option is from another builder');
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
new file mode 100644
index 0000000000..c14f445bf3
--- /dev/null
+++ b/testing/web-platform/tests/webnn/validation_tests/convTranspose2d.https.any.js
@@ -0,0 +1,59 @@
+// META: title=validation tests for WebNN API convTranspose2d operation
+// META: global=window,dedicatedworker
+// META: script=../resources/utils_validation.js
+
+'use strict';
+
+// Example input in NCHW layout.
+const kExampleInputDescriptor = {
+ dataType: 'float32',
+ dimensions: [1, 1, 5, 5]
+};
+// Example filter in OIHW layout.
+const kExampleFilterDescriptor = {
+ dataType: 'float32',
+ dimensions: [1, 1, 3, 3]
+};
+const kExampleBiasDescriptor = {
+ dataType: 'float32',
+ dimensions: [/* output channels */ 1]
+};
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const inputFromOtherBuilder =
+ otherBuilder.input('input', kExampleInputDescriptor);
+
+ const filter = builder.input('filter', kExampleFilterDescriptor);
+ assert_throws_js(
+ TypeError, () => builder.convTranspose2d(inputFromOtherBuilder, filter));
+}, '[convTranspose2d] throw if input is from another builder');
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const filterFromOtherBuilder =
+ otherBuilder.input('filter', kExampleFilterDescriptor);
+
+ const input = builder.input('input', kExampleInputDescriptor);
+ assert_throws_js(
+ TypeError, () => builder.convTranspose2d(input, filterFromOtherBuilder));
+}, '[convTranspose2d] throw if filter is from another builder');
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const biasFromOtherBuilder =
+ otherBuilder.input('bias', kExampleBiasDescriptor);
+ const options = {inputLayout: 'nchw', bias: biasFromOtherBuilder};
+
+ const input = builder.input('input', kExampleInputDescriptor);
+ const filter = builder.input('filter', kExampleFilterDescriptor);
+ assert_throws_js(
+ TypeError, () => builder.convTranspose2d(input, filter, options));
+}, '[convTranspose2d] throw if bias option is from another builder');
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const activationFromOtherBuilder = otherBuilder.clamp();
+ const options = {activation: activationFromOtherBuilder};
+
+ const input = builder.input('input', kExampleInputDescriptor);
+ const filter = builder.input('filter', kExampleFilterDescriptor);
+ assert_throws_js(
+ TypeError, () => builder.convTranspose2d(input, filter, options));
+}, '[convTranspose2d] throw if activation option is from another builder');
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..f54626b50d
--- /dev/null
+++ b/testing/web-platform/tests/webnn/validation_tests/elementwise-binary.https.any.js
@@ -0,0 +1,21 @@
+// META: title=validation tests for WebNN API element-wise binary operations
+// META: global=window,dedicatedworker
+// META: script=../resources/utils_validation.js
+
+'use strict';
+
+const kElementwiseBinaryOperators = [
+ 'add',
+ 'sub',
+ 'mul',
+ 'div',
+ 'max',
+ 'min',
+ 'pow',
+];
+
+kElementwiseBinaryOperators.forEach((operatorName) => {
+ validateTwoInputsOfSameDataType(operatorName);
+ validateTwoInputsBroadcastable(operatorName);
+ validateTwoInputsFromMultipleBuilders(operatorName);
+});
diff --git a/testing/web-platform/tests/webnn/validation_tests/elementwise-logical.https.any copy.js b/testing/web-platform/tests/webnn/validation_tests/elementwise-logical.https.any copy.js
new file mode 100644
index 0000000000..86183b074a
--- /dev/null
+++ b/testing/web-platform/tests/webnn/validation_tests/elementwise-logical.https.any copy.js
@@ -0,0 +1,20 @@
+// META: title=validation tests for WebNN API element-wise logical operations
+// META: global=window,dedicatedworker
+// META: script=../resources/utils_validation.js
+
+'use strict';
+
+const kElementwiseLogicalBinaryOperators = [
+ 'equal',
+ 'greater',
+ 'greaterOrEqual',
+ 'lesser',
+ 'lesserOrEqual',
+];
+
+kElementwiseLogicalBinaryOperators.forEach((operatorName) => {
+ validateTwoInputsFromMultipleBuilders(operatorName);
+});
+
+// The `not()` operator is unary.
+validateInputFromOtherBuilder('not');
diff --git a/testing/web-platform/tests/webnn/validation_tests/elementwise-unary.https.any.js b/testing/web-platform/tests/webnn/validation_tests/elementwise-unary.https.any.js
new file mode 100644
index 0000000000..f87c61b4e4
--- /dev/null
+++ b/testing/web-platform/tests/webnn/validation_tests/elementwise-unary.https.any.js
@@ -0,0 +1,39 @@
+// META: title=validation tests for WebNN API element-wise unary operations
+// META: global=window,dedicatedworker
+// META: script=../resources/utils_validation.js
+
+'use strict';
+
+const kElementwiseUnaryOperators = [
+ 'abs', 'ceil', 'cos', 'erf', 'exp', 'floor', 'identity', 'log', 'neg',
+ 'reciprocal', 'sin', 'sqrt', 'tan'
+];
+
+kElementwiseUnaryOperators.forEach((operatorName) => {
+ validateInputFromAnotherBuilder(operatorName);
+});
+
+const kElementwiseUnaryOperations = [
+ {
+ name: 'abs',
+ supportedDataTypes: [...floatingPointTypes, ...signedIntegerTypes]
+ },
+ {name: 'ceil', supportedDataTypes: floatingPointTypes},
+ {name: 'exp', supportedDataTypes: floatingPointTypes},
+ {name: 'floor', supportedDataTypes: floatingPointTypes},
+ {name: 'log', supportedDataTypes: floatingPointTypes}, {
+ name: 'neg',
+ supportedDataTypes: [...floatingPointTypes, ...signedIntegerTypes]
+ },
+ {name: 'sin', supportedDataTypes: floatingPointTypes},
+ {name: 'tan', supportedDataTypes: floatingPointTypes},
+ {name: 'erf', supportedDataTypes: floatingPointTypes},
+ {name: 'identity', supportedDataTypes: allWebNNOperandDataTypes},
+ {name: 'logicalNot', supportedDataTypes: ['uint8']},
+ {name: 'reciprocal', supportedDataTypes: floatingPointTypes},
+ {name: 'sqrt', supportedDataTypes: floatingPointTypes}
+];
+
+kElementwiseUnaryOperations.forEach((operation) => {
+ validateUnaryOperation(operation.name, operation.supportedDataTypes);
+});
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
deleted file mode 100644
index 97a1a2b93c..0000000000
--- a/testing/web-platform/tests/webnn/validation_tests/elementwise_binary.https.any.js
+++ /dev/null
@@ -1,11 +0,0 @@
-// 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/elu.https.any.js b/testing/web-platform/tests/webnn/validation_tests/elu.https.any.js
new file mode 100644
index 0000000000..6e842cb691
--- /dev/null
+++ b/testing/web-platform/tests/webnn/validation_tests/elu.https.any.js
@@ -0,0 +1,7 @@
+// META: title=validation tests for WebNN API elu operation
+// META: global=window,dedicatedworker
+// META: script=../resources/utils_validation.js
+
+'use strict';
+
+validateInputFromAnotherBuilder('elu');
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
new file mode 100644
index 0000000000..d90ab89468
--- /dev/null
+++ b/testing/web-platform/tests/webnn/validation_tests/expand.https.any.js
@@ -0,0 +1,14 @@
+// META: title=validation tests for WebNN API expand operation
+// META: global=window,dedicatedworker
+// META: script=../resources/utils_validation.js
+
+'use strict';
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const inputFromOtherBuilder =
+ otherBuilder.input('input', {dataType: 'float32', dimensions: [2, 1, 2]});
+
+ const newShape = [2, 2, 2];
+ assert_throws_js(
+ TypeError, () => builder.expand(inputFromOtherBuilder, newShape));
+}, '[expand] throw if input is from another builder');
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
index 67ac7d7be3..184e8033e6 100644
--- a/testing/web-platform/tests/webnn/validation_tests/gather.https.any.js
+++ b/testing/web-platform/tests/webnn/validation_tests/gather.https.any.js
@@ -1,7 +1,6 @@
// META: title=validation tests for WebNN API gather operation
// META: global=window,dedicatedworker
// META: script=../resources/utils_validation.js
-// META: timeout=long
'use strict';
@@ -60,3 +59,23 @@ tests.forEach(
TypeError, () => builder.gather(input, indices, options));
}
}, test.name));
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const inputFromOtherBuilder =
+ otherBuilder.input('input', {dataType: 'float32', dimensions: [2, 2]});
+
+ const indices =
+ builder.input('indices', {dataType: 'int64', dimensions: [2, 2]});
+ assert_throws_js(
+ TypeError, () => builder.gather(inputFromOtherBuilder, indices));
+}, '[gather] throw if input is from another builder');
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const indicesFromOtherBuilder =
+ otherBuilder.input('indices', {dataType: 'int64', dimensions: [2, 2]});
+
+ const input =
+ builder.input('input', {dataType: 'float32', dimensions: [2, 2]});
+ assert_throws_js(
+ TypeError, () => builder.gather(input, indicesFromOtherBuilder));
+}, '[gather] throw if indices is from another builder');
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
new file mode 100644
index 0000000000..77ce6383cc
--- /dev/null
+++ b/testing/web-platform/tests/webnn/validation_tests/gemm.https.any.js
@@ -0,0 +1,21 @@
+// META: title=validation tests for WebNN API gemm operation
+// META: global=window,dedicatedworker
+// META: script=../resources/utils_validation.js
+
+'use strict';
+
+const kExampleInputDescriptor = {
+ dataType: 'float32',
+ dimensions: [2, 2]
+};
+
+validateTwoInputsFromMultipleBuilders('gemm');
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const cFromOtherBuilder = otherBuilder.input('c', kExampleInputDescriptor);
+ const options = {c: cFromOtherBuilder};
+
+ const a = builder.input('a', kExampleInputDescriptor);
+ const b = builder.input('b', kExampleInputDescriptor);
+ assert_throws_js(TypeError, () => builder.gemm(a, b, options));
+}, '[gemm] throw if c option is from another builder');
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
index 295baab9c2..50d39d297a 100644
--- a/testing/web-platform/tests/webnn/validation_tests/gru.https.any.js
+++ b/testing/web-platform/tests/webnn/validation_tests/gru.https.any.js
@@ -1,398 +1,445 @@
// 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 steps = 2, batchSize = 3, inputSize = 4, hiddenSize = 5, oneDirection = 1,
+ bothDirections = 2;
+
+// Dimensions required of required inputs.
+const kValidInputDimensions = [steps, batchSize, inputSize];
+const kValidWeightDimensions = [oneDirection, 3 * hiddenSize, inputSize];
+const kValidRecurrentWeightDimensions =
+ [oneDirection, 3 * hiddenSize, hiddenSize];
+// Dimensions required of optional inputs.
+const kValidBiasDimensions = [oneDirection, 3 * hiddenSize];
+const kValidRecurrentBiasDimensions = [oneDirection, 3 * hiddenSize];
+const kValidInitialHiddenStateDimensions =
+ [oneDirection, batchSize, hiddenSize];
+
+// Example descriptors which are valid according to the above dimensions.
+const kExampleInputDescriptor = {
+ dataType: 'float32',
+ dimensions: kValidInputDimensions
+};
+const kExampleWeightDescriptor = {
+ dataType: 'float32',
+ dimensions: kValidWeightDimensions
+};
+const kExampleRecurrentWeightDescriptor = {
+ dataType: 'float32',
+ dimensions: kValidRecurrentWeightDimensions
+};
+const kExampleBiasDescriptor = {
+ dataType: 'float32',
+ dimensions: kValidBiasDimensions
+};
+const kExampleRecurrentBiasDescriptor = {
+ dataType: 'float32',
+ dimensions: kValidRecurrentBiasDimensions
+};
+const kExampleInitialHiddenStateDescriptor = {
+ dataType: 'float32',
+ dimensions: kValidInitialHiddenStateDimensions
+};
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] Test with default options',
+ input: kExampleInputDescriptor,
+ weight: kExampleWeightDescriptor,
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
+ steps: steps,
+ hiddenSize: hiddenSize,
+ outputs: [
+ {dataType: 'float32', dimensions: [oneDirection, batchSize, hiddenSize]}
+ ]
+ },
+ {
+ name: '[gru] Test with given options',
+ input: kExampleInputDescriptor,
+ weight: {
+ dataType: 'float32',
+ dimensions: [bothDirections, 3 * hiddenSize, inputSize]
},
- {
- 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
+ recurrentWeight: {
+ dataType: 'float32',
+ dimensions: [bothDirections, 3 * 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
+ steps: steps,
+ hiddenSize: hiddenSize,
+ options: {
+ bias: {dataType: 'float32', dimensions: [bothDirections, 3 * hiddenSize]},
+ recurrentBias:
+ {dataType: 'float32', dimensions: [bothDirections, 3 * hiddenSize]},
+ initialHiddenState: {
+ dataType: 'float32',
+ dimensions: [bothDirections, batchSize, hiddenSize]
+ },
+ restAfter: true,
+ returnSequence: true,
+ direction: 'both',
+ layout: 'rzn',
+ activations: ['sigmoid', 'relu']
},
- {
- 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
+ outputs: [
+ {
+ dataType: 'float32',
+ dimensions: [bothDirections, batchSize, hiddenSize]
+ },
+ {
+ dataType: 'float32',
+ dimensions: [steps, bothDirections, batchSize, hiddenSize]
+ }
+ ]
+ },
+ {
+ name: '[gru] TypeError is expected if steps equals to zero',
+ input: kExampleInputDescriptor,
+ weight: {
+ dataType: 'float32',
+ dimensions: [oneDirection, 4 * hiddenSize, inputSize]
},
- {
- 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
+ recurrentWeight: {
+ dataType: 'float32',
+ dimensions: [oneDirection, 4 * 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
+ steps: 0,
+ hiddenSize: hiddenSize,
+ },
+ {
+ name: '[gru] TypeError is expected if hiddenSize equals to zero',
+ input: kExampleInputDescriptor,
+ weight: kExampleWeightDescriptor,
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
+ steps: steps,
+ hiddenSize: 0
+ },
+ {
+ name: '[gru] TypeError is expected if hiddenSize is too large',
+ input: kExampleInputDescriptor,
+ weight: kExampleWeightDescriptor,
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
+ 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: kValidInputDimensions},
+ weight: {dataType: 'uint32', dimensions: kValidWeightDimensions},
+ recurrentWeight:
+ {dataType: 'uint32', dimensions: kValidRecurrentWeightDimensions},
+ steps: steps,
+ hiddenSize: hiddenSize
+ },
+ {
+ name: '[gru] TypeError is expected if the rank of input is not 3',
+ input: {dataType: 'float32', dimensions: [steps, batchSize]},
+ weight: kExampleWeightDescriptor,
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
+ 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: kExampleWeightDescriptor,
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
+ steps: steps,
+ hiddenSize: hiddenSize
+ },
+ {
+ name:
+ '[gru] TypeError is expected if weight.dimensions[1] is not 3 * hiddenSize',
+ input: kExampleInputDescriptor,
+ weight: {
+ dataType: 'float32',
+ dimensions: [oneDirection, 4 * hiddenSize, inputSize]
},
- {
- 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] }
- }
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
+ steps: steps,
+ hiddenSize: hiddenSize
+ },
+ {
+ name: '[gru] TypeError is expected if the rank of recurrentWeight is not 3',
+ input: kExampleInputDescriptor,
+ weight: kExampleWeightDescriptor,
+ recurrentWeight:
+ {dataType: 'float32', dimensions: [oneDirection, 3 * hiddenSize]},
+ steps: steps,
+ hiddenSize: hiddenSize
+ },
+ {
+ name:
+ '[gru] TypeError is expected if the recurrentWeight.dimensions is invalid',
+ input: kExampleInputDescriptor,
+ weight: kExampleWeightDescriptor,
+ recurrentWeight: {
+ dataType: 'float32',
+ dimensions: [oneDirection, 4 * hiddenSize, inputSize]
},
- {
- 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]
- }
- }
+ steps: steps,
+ hiddenSize: hiddenSize
+ },
+ {
+ name:
+ '[gru] TypeError is expected if the size of options.activations is not 2',
+ input: kExampleInputDescriptor,
+ weight: kExampleWeightDescriptor,
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
+ steps: steps,
+ hiddenSize: hiddenSize,
+ options: {activations: ['sigmoid', 'tanh', 'relu']}
+ },
+ {
+ name: '[gru] TypeError is expected if the rank of options.bias is not 2',
+ input: kExampleInputDescriptor,
+ weight: kExampleWeightDescriptor,
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
+ steps: steps,
+ hiddenSize: hiddenSize,
+ options: {bias: {dataType: 'float32', dimensions: [oneDirection]}}
+ },
+ {
+ name:
+ '[gru] TypeError is expected if options.bias.dimensions[1] is not 3 * hiddenSize',
+ input: kExampleInputDescriptor,
+ weight: kExampleWeightDescriptor,
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
+ steps: steps,
+ hiddenSize: hiddenSize,
+ options:
+ {bias: {dataType: 'float32', dimensions: [oneDirection, hiddenSize]}}
+ },
+ {
+ name:
+ '[gru] TypeError is expected if options.recurrentBias.dimensions[1] is not 3 * hiddenSize',
+ input: {dataType: 'float16', dimensions: kValidInputDimensions},
+ weight: {dataType: 'float16', dimensions: kValidWeightDimensions},
+ recurrentWeight:
+ {dataType: 'float16', dimensions: kValidRecurrentWeightDimensions},
+ steps: steps,
+ hiddenSize: hiddenSize,
+ options: {
+ recurrentBias:
+ {dataType: 'float16', dimensions: [oneDirection, 4 * hiddenSize]}
}
+ },
+ {
+ name:
+ '[gru] TypeError is expected if the rank of options.initialHiddenState is not 3',
+ input: {dataType: 'float16', dimensions: kValidInputDimensions},
+ weight: {dataType: 'float16', dimensions: kValidWeightDimensions},
+ recurrentWeight:
+ {dataType: 'float16', dimensions: kValidRecurrentWeightDimensions},
+ steps: steps,
+ hiddenSize: hiddenSize,
+ options: {
+ initialHiddenState:
+ {dataType: 'float16', dimensions: [oneDirection, batchSize]}
+ }
+ },
+ {
+ name:
+ '[gru] TypeError is expected if options.initialHiddenState.dimensions[2] is not inputSize',
+ input: {dataType: 'float16', dimensions: kValidInputDimensions},
+ weight: {dataType: 'float16', dimensions: kValidWeightDimensions},
+ recurrentWeight:
+ {dataType: 'float16', dimensions: kValidRecurrentWeightDimensions},
+ steps: steps,
+ hiddenSize: hiddenSize,
+ options: {
+ initialHiddenState: {
+ dataType: 'float16',
+ dimensions: [oneDirection, batchSize, 3 * hiddenSize]
+ }
+ }
+ },
+ {
+ name:
+ '[gru] TypeError is expected if the dataType of options.initialHiddenState is incorrect',
+ input: {dataType: 'float16', dimensions: kValidInputDimensions},
+ weight: {dataType: 'float16', dimensions: kValidWeightDimensions},
+ recurrentWeight:
+ {dataType: 'float16', dimensions: kValidRecurrentWeightDimensions},
+ steps: steps,
+ hiddenSize: hiddenSize,
+ options: {
+ initialHiddenState: {
+ dataType: 'uint64',
+ dimensions: [oneDirection, 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 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]()));
- }
+ 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));
+ 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);
}
- }, test.name)); \ No newline at end of file
+ } else {
+ assert_throws_js(
+ TypeError,
+ () => builder.gru(
+ input, weight, recurrentWeight, test.steps, test.hiddenSize,
+ options));
+ }
+ }, test.name));
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const inputFromOtherBuilder =
+ otherBuilder.input('input', kExampleInputDescriptor);
+
+ const weight = builder.input('weight', kExampleWeightDescriptor);
+ const recurrentWeight =
+ builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor);
+ assert_throws_js(
+ TypeError,
+ () => builder.gru(
+ inputFromOtherBuilder, weight, recurrentWeight, steps, hiddenSize));
+}, '[gru] throw if input is from another builder');
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const weightFromOtherBuilder =
+ otherBuilder.input('weight', kExampleWeightDescriptor);
+
+ const input = builder.input('input', kExampleInputDescriptor);
+ const recurrentWeight =
+ builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor);
+ assert_throws_js(
+ TypeError,
+ () => builder.gru(
+ input, weightFromOtherBuilder, recurrentWeight, steps, hiddenSize));
+}, '[gru] throw if weight is from another builder');
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const recurrentWeightFromOtherBuilder =
+ otherBuilder.input('recurrentWeight', kExampleRecurrentWeightDescriptor);
+
+ const input = builder.input('input', kExampleInputDescriptor);
+ const weight = builder.input('weight', kExampleWeightDescriptor);
+ assert_throws_js(
+ TypeError,
+ () => builder.gru(
+ input, weight, recurrentWeightFromOtherBuilder, steps, hiddenSize));
+}, '[gru] throw if recurrentWeight is from another builder');
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const biasFromOtherBuilder =
+ otherBuilder.input('bias', kExampleBiasDescriptor);
+ const options = {bias: biasFromOtherBuilder};
+
+ const input = builder.input('input', kExampleInputDescriptor);
+ const weight = builder.input('weight', kExampleWeightDescriptor);
+ const recurrentWeight =
+ builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor);
+ assert_throws_js(
+ TypeError,
+ () => builder.gru(
+ input, weight, recurrentWeight, steps, hiddenSize, options));
+}, '[gru] throw if bias option is from another builder');
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const recurrentBiasFromOtherBuilder =
+ otherBuilder.input('recurrentBias', kExampleRecurrentBiasDescriptor);
+ const options = {recurrentBias: recurrentBiasFromOtherBuilder};
+
+ const input = builder.input('input', kExampleInputDescriptor);
+ const weight = builder.input('weight', kExampleWeightDescriptor);
+ const recurrentWeight =
+ builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor);
+ assert_throws_js(
+ TypeError,
+ () => builder.gru(
+ input, weight, recurrentWeight, steps, hiddenSize, options));
+}, '[gru] throw if recurrentBias option is from another builder');
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const initialHiddenStateFromOtherBuilder = otherBuilder.input(
+ 'initialHiddenState', kExampleInitialHiddenStateDescriptor);
+ const options = {initialHiddenState: initialHiddenStateFromOtherBuilder};
+
+ const input = builder.input('input', kExampleInputDescriptor);
+ const weight = builder.input('weight', kExampleWeightDescriptor);
+ const recurrentWeight =
+ builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor);
+ assert_throws_js(
+ TypeError,
+ () => builder.gru(
+ input, weight, recurrentWeight, steps, hiddenSize, options));
+}, '[gru] throw if initialHiddenState option is from another builder');
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const activation = builder.clamp();
+ const activationFromOtherBuilder = otherBuilder.clamp();
+ const options = {activations: [activation, activationFromOtherBuilder]};
+
+ const input = builder.input('input', kExampleInputDescriptor);
+ const weight = builder.input('weight', kExampleWeightDescriptor);
+ const recurrentWeight =
+ builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor);
+ assert_throws_js(
+ TypeError,
+ () => builder.gru(
+ input, weight, recurrentWeight, steps, hiddenSize, options));
+}, '[gru] throw if any activation option is from another builder');
diff --git a/testing/web-platform/tests/webnn/validation_tests/gruCell.https.any.js b/testing/web-platform/tests/webnn/validation_tests/gruCell.https.any.js
new file mode 100644
index 0000000000..3cd9d32b07
--- /dev/null
+++ b/testing/web-platform/tests/webnn/validation_tests/gruCell.https.any.js
@@ -0,0 +1,471 @@
+// META: title=validation tests for WebNN API gruCell operation
+// META: global=window,dedicatedworker
+// META: script=../resources/utils_validation.js
+
+'use strict';
+
+const batchSize = 3, inputSize = 4, hiddenSize = 5;
+
+// Dimensions required of required inputs.
+const kValidInputDimensions = [batchSize, inputSize];
+const kValidWeightDimensions = [3 * hiddenSize, inputSize];
+const kValidRecurrentWeightDimensions = [3 * hiddenSize, hiddenSize];
+const kValidHiddenStateDimensions = [batchSize, hiddenSize];
+// Dimensions required of optional inputs.
+const kValidBiasDimensions = [3 * hiddenSize];
+const kValidRecurrentBiasDimensions = [3 * hiddenSize];
+// Dimensions required of required output.
+const kValidOutputDimensions = [batchSize, hiddenSize];
+
+// Example descriptors which are valid according to the above dimensions.
+const kExampleInputDescriptor = {
+ dataType: 'float32',
+ dimensions: kValidInputDimensions
+};
+const kExampleWeightDescriptor = {
+ dataType: 'float32',
+ dimensions: kValidWeightDimensions
+};
+const kExampleRecurrentWeightDescriptor = {
+ dataType: 'float32',
+ dimensions: kValidRecurrentWeightDimensions
+};
+const kExampleHiddenStateDescriptor = {
+ dataType: 'float32',
+ dimensions: kValidHiddenStateDimensions
+};
+const kExampleBiasDescriptor = {
+ dataType: 'float32',
+ dimensions: kValidBiasDimensions
+};
+const kExampleRecurrentBiasDescriptor = {
+ dataType: 'float32',
+ dimensions: kValidRecurrentBiasDimensions
+};
+const kExampleOutputDescriptor = {
+ dataType: 'float32',
+ dimensions: kValidOutputDimensions
+ };
+
+const tests = [
+ {
+ name: '[gruCell] Test with default options',
+ input: kExampleInputDescriptor,
+ weight: kExampleWeightDescriptor,
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
+ hiddenState: kExampleHiddenStateDescriptor,
+ hiddenSize: hiddenSize,
+ output: kExampleOutputDescriptor
+ },
+ {
+ name: '[gruCell] Test with given options',
+ input: kExampleInputDescriptor,
+ weight: kExampleWeightDescriptor,
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
+ hiddenState: kExampleHiddenStateDescriptor,
+ hiddenSize: hiddenSize,
+ options: {
+ bias: kExampleBiasDescriptor,
+ recurrentBias: kExampleRecurrentBiasDescriptor,
+ restAfter: true,
+ layout: 'rzn',
+ activations: ['sigmoid', 'relu']
+ },
+ output: kExampleOutputDescriptor
+ },
+ {
+ name: '[gruCell] Throw if hiddenSize equals to zero',
+ input: kExampleInputDescriptor,
+ weight: kExampleWeightDescriptor,
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
+ hiddenState: kExampleHiddenStateDescriptor,
+ hiddenSize: 0
+ },
+ {
+ name: '[gruCell] Throw if hiddenSize is too large',
+ input: kExampleInputDescriptor,
+ weight: kExampleWeightDescriptor,
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
+ hiddenState: kExampleHiddenStateDescriptor,
+ hiddenSize: 4294967295,
+ },
+ {
+ name:
+ '[gruCell] Throw if the data type of the inputs is not one of the floating point types',
+ input: { dataType: 'uint32', dimensions: kValidInputDimensions },
+ weight: { dataType: 'uint32', dimensions: kValidWeightDimensions },
+ recurrentWeight: {
+ dataType: 'uint32',
+ dimensions: kValidRecurrentWeightDimensions
+ },
+ hiddenState: {
+ dataType: 'uint32',
+ dimensions: kValidHiddenStateDimensions
+ },
+ hiddenSize: hiddenSize
+ },
+ {
+ name:
+ '[gruCell] Throw if the rank of input is not 2',
+ input: { dataType: 'float32', dimensions: [batchSize] },
+ weight: kExampleWeightDescriptor,
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
+ hiddenState: kExampleHiddenStateDescriptor,
+ hiddenSize: hiddenSize
+ },
+ {
+ name:
+ '[gruCell] Throw if the input.dimensions[1] is incorrect',
+ input: { dataType: 'float32', dimensions: [inputSize, inputSize] },
+ weight: kExampleWeightDescriptor,
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
+ hiddenState: kExampleHiddenStateDescriptor,
+ hiddenSize: hiddenSize
+ },
+ {
+ name: '[gruCell] Throw if data type of weight is not one of the floating point types',
+ input: kExampleInputDescriptor,
+ weight: {
+ dataType: 'int8',
+ dimensions: [3 * hiddenSize, inputSize]
+ },
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
+ hiddenState: kExampleHiddenStateDescriptor,
+ hiddenSize: hiddenSize
+ },
+ {
+ name: '[gruCell] Throw if rank of weight is not 2',
+ input: kExampleInputDescriptor,
+ weight: {
+ dataType: 'float32',
+ dimensions: [3 * hiddenSize]
+ },
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
+ hiddenState: kExampleHiddenStateDescriptor,
+ hiddenSize: hiddenSize
+ },
+ {
+ name: '[gruCell] Throw if weight.dimensions[0] is not 3 * hiddenSize',
+ input: kExampleInputDescriptor,
+ weight: {
+ dataType: 'float32',
+ dimensions: [4 * hiddenSize, inputSize]
+ },
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
+ hiddenState: kExampleHiddenStateDescriptor,
+ hiddenSize: hiddenSize
+ },
+ {
+ name: '[gruCell] Throw if data type of recurrentWeight is not one of the floating point types',
+ input: kExampleInputDescriptor,
+ weight: kExampleWeightDescriptor,
+ recurrentWeight: {
+ dataType: 'int32',
+ dimensions: [3 * hiddenSize, hiddenSize]
+ },
+ hiddenState: kExampleHiddenStateDescriptor,
+ hiddenSize: hiddenSize
+ },
+ {
+ name:
+ '[gruCell] Throw if the rank of recurrentWeight is not 2',
+ input: kExampleInputDescriptor,
+ weight: kExampleWeightDescriptor,
+ recurrentWeight:
+ { dataType: 'float32', dimensions: [3 * hiddenSize] },
+ hiddenState: kExampleHiddenStateDescriptor,
+ hiddenSize: hiddenSize
+ },
+ {
+ name:
+ '[gruCell] Throw if the recurrentWeight.dimensions is invalid',
+ input: kExampleInputDescriptor,
+ weight: kExampleWeightDescriptor,
+ recurrentWeight:
+ { dataType: 'float32', dimensions: [4 * hiddenSize, inputSize] },
+ hiddenState: kExampleHiddenStateDescriptor,
+ hiddenSize: hiddenSize
+ },
+ {
+ name:
+ '[gruCell] Throw if data type of hiddenState is not one of the floating point types',
+ input: kExampleInputDescriptor,
+ weight: kExampleWeightDescriptor,
+ recurrentWeight:
+ kExampleRecurrentWeightDescriptor,
+ hiddenState: {
+ dataType: 'uint32',
+ dimensions: [batchSize, hiddenSize]
+ },
+ hiddenSize: hiddenSize
+ },
+ {
+ name:
+ '[gruCell] Throw if the rank of hiddenState is not 2',
+ input: kExampleInputDescriptor,
+ weight: kExampleWeightDescriptor,
+ recurrentWeight:
+ kExampleRecurrentWeightDescriptor,
+ hiddenState: {
+ dataType: 'float32',
+ dimensions: [hiddenSize]
+ },
+ hiddenSize: hiddenSize
+ },
+ {
+ name:
+ '[gruCell] Throw if the hiddenState.dimensions is invalid',
+ input: kExampleInputDescriptor,
+ weight: kExampleWeightDescriptor,
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
+ hiddenState: {
+ dataType: 'float32',
+ dimensions: [batchSize, 3 * hiddenSize]
+ },
+ hiddenSize: hiddenSize
+ },
+ {
+ name:
+ '[gruCell] Throw if the size of options.activations is not 2',
+ input: kExampleInputDescriptor,
+ weight: kExampleWeightDescriptor,
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
+ hiddenState: kExampleHiddenStateDescriptor,
+ hiddenSize: hiddenSize,
+ options: { activations: ['sigmoid', 'tanh', 'relu'] }
+ },
+ {
+ name:
+ '[gruCell] Throw if data type of options.bias is not one of the floating point types',
+ input: kExampleInputDescriptor,
+ weight: kExampleWeightDescriptor,
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
+ hiddenState: kExampleHiddenStateDescriptor,
+ hiddenSize: hiddenSize,
+ options: { bias: { dataType: 'uint8', dimensions: [3 * hiddenSize] } }
+ },
+ {
+ name:
+ '[gruCell] Throw if the rank of options.bias is not 1',
+ input: kExampleInputDescriptor,
+ weight: kExampleWeightDescriptor,
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
+ hiddenState: kExampleHiddenStateDescriptor,
+ hiddenSize: hiddenSize,
+ options: { bias: { dataType: 'float32', dimensions: [batchSize, 3 * hiddenSize] } }
+ },
+ {
+ name:
+ '[gruCell] Throw if options.bias.dimensions[0] is not 3 * hiddenSize',
+ input: kExampleInputDescriptor,
+ weight: kExampleWeightDescriptor,
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
+ hiddenState: kExampleHiddenStateDescriptor,
+ hiddenSize: hiddenSize,
+ options: { bias: { dataType: 'float32', dimensions: [2 * hiddenSize] } }
+ },
+ {
+ name:
+ '[gruCell] Throw if data type of options.recurrentBias is not one of the floating point types',
+ input: kExampleInputDescriptor,
+ weight: kExampleWeightDescriptor,
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
+ hiddenState: kExampleHiddenStateDescriptor,
+ hiddenSize: hiddenSize,
+ options: { recurrentBias: { dataType: 'int8', dimensions: [3 * hiddenSize] } }
+ },
+ {
+ name:
+ '[gruCell] Throw if the rank of options.recurrentBias is not 1',
+ input: kExampleInputDescriptor,
+ weight: kExampleWeightDescriptor,
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
+ hiddenState: kExampleHiddenStateDescriptor,
+ hiddenSize: hiddenSize,
+ options: { recurrentBias: { dataType: 'float32', dimensions: [batchSize, 3 * hiddenSize] } }
+ },
+ {
+ name:
+ '[gruCell] Throw if options.recurrentBias.dimensions[0] is not 3 * hiddenSize',
+ input: kExampleInputDescriptor,
+ weight: kExampleWeightDescriptor,
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
+ hiddenState: kExampleHiddenStateDescriptor,
+ hiddenSize: hiddenSize,
+ options: {
+ recurrentBias: { dataType: 'float16', dimensions: [4 * 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 hiddenState = builder.input('hiddenState', {
+ dataType: test.hiddenState.dataType,
+ dimensions: test.hiddenState.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.resetAfter) {
+ options.resetAfter = test.options.resetAfter;
+ }
+ 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.output) {
+ const output = builder.gruCell(
+ input, weight, recurrentWeight, hiddenState, test.hiddenSize,
+ options);
+ assert_equals(output.dataType(), test.output.dataType);
+ assert_array_equals(output.shape(), test.output.dimensions);
+ } else {
+ assert_throws_js(
+ TypeError,
+ () => builder.gruCell(
+ input, weight, recurrentWeight, hiddenState, test.hiddenSize,
+ options));
+ }
+ }, test.name));
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const inputFromOtherBuilder =
+ otherBuilder.input('input', kExampleInputDescriptor);
+
+ const weight = builder.input('weight', kExampleWeightDescriptor);
+ const recurrentWeight =
+ builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor);
+ const hiddenState =
+ builder.input('hiddenState', kExampleHiddenStateDescriptor);
+ assert_throws_js(
+ TypeError,
+ () => builder.gruCell(
+ inputFromOtherBuilder, weight, recurrentWeight, hiddenState,
+ hiddenSize));
+}, '[gruCell] throw if input is from another builder');
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const weightFromOtherBuilder =
+ otherBuilder.input('weight', kExampleWeightDescriptor);
+
+ const input = builder.input('input', kExampleInputDescriptor);
+ const recurrentWeight =
+ builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor);
+ const hiddenState =
+ builder.input('hiddenState', kExampleHiddenStateDescriptor);
+ assert_throws_js(
+ TypeError,
+ () => builder.gruCell(
+ input, weightFromOtherBuilder, recurrentWeight, hiddenState,
+ hiddenSize));
+}, '[gruCell] throw if weight is from another builder');
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const recurrentWeightFromOtherBuilder =
+ otherBuilder.input('recurrentWeight', kExampleRecurrentWeightDescriptor);
+
+ const input = builder.input('input', kExampleInputDescriptor);
+ const weight = builder.input('weight', kExampleWeightDescriptor);
+ const hiddenState =
+ builder.input('hiddenState', kExampleHiddenStateDescriptor);
+ assert_throws_js(
+ TypeError,
+ () => builder.gruCell(
+ input, weight, recurrentWeightFromOtherBuilder, hiddenState,
+ hiddenSize));
+}, '[gruCell] throw if recurrentWeight is from another builder');
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const hiddenStateFromOtherBuilder =
+ otherBuilder.input('hiddenState', kExampleHiddenStateDescriptor);
+
+ const input = builder.input('input', kExampleInputDescriptor);
+ const weight = builder.input('weight', kExampleWeightDescriptor);
+ const recurrentWeight =
+ builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor);
+ assert_throws_js(
+ TypeError,
+ () => builder.gruCell(
+ input, weight, recurrentWeight, hiddenStateFromOtherBuilder,
+ hiddenSize));
+}, '[gruCell] throw if hiddenState is from another builder');
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const biasFromOtherBuilder =
+ otherBuilder.input('bias', kExampleBiasDescriptor);
+ const options = {bias: biasFromOtherBuilder};
+
+ const input = builder.input('input', kExampleInputDescriptor);
+ const weight = builder.input('weight', kExampleWeightDescriptor);
+ const recurrentWeight =
+ builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor);
+ const hiddenState =
+ builder.input('hiddenState', kExampleHiddenStateDescriptor);
+ assert_throws_js(
+ TypeError,
+ () => builder.gruCell(
+ input, weight, recurrentWeight, hiddenState, hiddenSize, options));
+}, '[gruCell] throw if bias option is from another builder');
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const recurrentBiasFromOtherBuilder =
+ otherBuilder.input('recurrentBias', kExampleRecurrentBiasDescriptor);
+ const options = {recurrentBias: recurrentBiasFromOtherBuilder};
+
+ const input = builder.input('input', kExampleInputDescriptor);
+ const weight = builder.input('weight', kExampleWeightDescriptor);
+ const recurrentWeight =
+ builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor);
+ const hiddenState =
+ builder.input('hiddenState', kExampleHiddenStateDescriptor);
+ assert_throws_js(
+ TypeError,
+ () => builder.gruCell(
+ input, weight, recurrentWeight, hiddenState, hiddenSize, options));
+}, '[gruCell] throw if recurrentBias option is from another builder');
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const activation = builder.clamp();
+ const activationFromOtherBuilder = otherBuilder.clamp();
+ const options = {activations: [activation, activationFromOtherBuilder]};
+
+ const input = builder.input('input', kExampleInputDescriptor);
+ const weight = builder.input('weight', kExampleWeightDescriptor);
+ const recurrentWeight =
+ builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor);
+ const hiddenState =
+ builder.input('hiddenState', kExampleHiddenStateDescriptor);
+ assert_throws_js(
+ TypeError,
+ () => builder.gruCell(
+ input, weight, recurrentWeight, hiddenState, hiddenSize, options));
+}, '[gruCell] throw if any activation option is from another builder');
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
new file mode 100644
index 0000000000..01b24dbc7c
--- /dev/null
+++ b/testing/web-platform/tests/webnn/validation_tests/hardSigmoid.https.any.js
@@ -0,0 +1,7 @@
+// META: title=validation tests for WebNN API hardSigmoid operation
+// META: global=window,dedicatedworker
+// META: script=../resources/utils_validation.js
+
+'use strict';
+
+validateInputFromAnotherBuilder('hardSigmoid');
diff --git a/testing/web-platform/tests/webnn/validation_tests/hardSwish.https.any.js b/testing/web-platform/tests/webnn/validation_tests/hardSwish.https.any.js
new file mode 100644
index 0000000000..97ecfb4142
--- /dev/null
+++ b/testing/web-platform/tests/webnn/validation_tests/hardSwish.https.any.js
@@ -0,0 +1,10 @@
+// META: title=validation tests for WebNN API hardSwish operation
+// META: global=window,dedicatedworker
+// META: script=../resources/utils_validation.js
+
+'use strict';
+
+validateInputFromAnotherBuilder('hardSwish');
+
+validateUnaryOperation(
+ 'hardSwish', floatingPointTypes, /*alsoBuildActivation=*/ true);
diff --git a/testing/web-platform/tests/webnn/validation_tests/input.https.any.js b/testing/web-platform/tests/webnn/validation_tests/input.https.any.js
new file mode 100644
index 0000000000..a7561bf628
--- /dev/null
+++ b/testing/web-platform/tests/webnn/validation_tests/input.https.any.js
@@ -0,0 +1,70 @@
+// META: title=validation tests for WebNN API input interface
+// META: global=window,dedicatedworker
+// META: script=../resources/utils_validation.js
+
+'use strict';
+
+// Tests for input(name, descriptor)
+const tests = [
+ {
+ testName:
+ '[input] Test building a 0-D scalar input without presenting dimensions',
+ name: 'input',
+ descriptor: {dataType: 'float32'},
+ output: {dataType: 'float32', dimensions: []},
+ },
+ {
+ testName: '[input] Test building a 0-D scalar input with empty dimensions',
+ name: 'input',
+ descriptor: {dataType: 'float32', dimensions: []},
+ output: {dataType: 'float32', dimensions: []},
+ },
+ {
+ testName: '[input] Test building a 1-D input with int64 data type',
+ name: 'input',
+ descriptor: {dataType: 'int64', dimensions: [3]},
+ output: {dataType: 'int64', dimensions: [3]},
+ },
+ {
+ testName: '[input] Test building a 2-D input without errors',
+ name: 'input',
+ descriptor: {dataType: 'float32', dimensions: [3, 4]},
+ output: {dataType: 'float32', dimensions: [3, 4]},
+ },
+ {
+ testName: '[input] Throw if the name is empty',
+ name: '',
+ descriptor: {dataType: 'float32', dimensions: [3, 4]}
+ },
+ {
+ testName: '[input] Throw if a dimension size is 0',
+ name: 'input',
+ descriptor: {dataType: 'float32', dimensions: [3, 0]}
+ },
+ {
+ testName:
+ '[input] Throw if the value of any element in dimensions is outside the \'unsigned long\' value range',
+ name: 'input',
+ descriptor: {dataType: 'float32', dimensions: [kMaxUnsignedLong + 1]}
+ },
+ {
+ testName: '[input] Throw if the number of elements is too large',
+ name: 'input',
+ descriptor: {
+ dataType: 'float32',
+ dimensions: [kMaxUnsignedLong, kMaxUnsignedLong, kMaxUnsignedLong]
+ }
+ }
+];
+
+tests.forEach(
+ test => promise_test(async t => {
+ if (test.output) {
+ const inputOperand = builder.input(test.name, test.descriptor);
+ assert_equals(inputOperand.dataType(), test.output.dataType);
+ assert_array_equals(inputOperand.shape(), test.output.dimensions);
+ } else {
+ assert_throws_js(
+ TypeError, () => builder.input(test.name, test.descriptor));
+ }
+ }, test.testName));
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
new file mode 100644
index 0000000000..bdd338588f
--- /dev/null
+++ b/testing/web-platform/tests/webnn/validation_tests/instanceNormalization.https.any.js
@@ -0,0 +1,43 @@
+// META: title=validation tests for WebNN API instanceNormalization operation
+// META: global=window,dedicatedworker
+// META: script=../resources/utils_validation.js
+
+'use strict';
+
+const kExampleInputDescriptor = {
+ dataType: 'float32',
+ dimensions: [2, 2, 2, 2]
+};
+// 1D tensor descriptor which may be used for `scale`, or `bias` inputs.
+const kExample1DTensorDescriptor = {
+ dataType: 'float32',
+ dimensions: [2]
+};
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const inputFromOtherBuilder =
+ otherBuilder.input('input', kExampleInputDescriptor);
+
+ assert_throws_js(
+ TypeError, () => builder.instanceNormalization(inputFromOtherBuilder));
+}, '[instanceNormalization] throw if input is from another builder');
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const scaleFromOtherBuilder =
+ otherBuilder.input('scale', kExample1DTensorDescriptor);
+ const options = {scale: scaleFromOtherBuilder};
+
+ const input = builder.input('input', kExampleInputDescriptor);
+ assert_throws_js(
+ TypeError, () => builder.instanceNormalization(input, options));
+}, '[instanceNormalization] throw if scale option is from another builder');
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const biasFromOtherBuilder =
+ otherBuilder.input('bias', kExample1DTensorDescriptor);
+ const options = {bias: biasFromOtherBuilder};
+
+ const input = builder.input('input', kExampleInputDescriptor);
+ assert_throws_js(
+ TypeError, () => builder.instanceNormalization(input, options));
+}, '[instanceNormalization] throw if bias option is from another builder');
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
new file mode 100644
index 0000000000..e9e9141aa6
--- /dev/null
+++ b/testing/web-platform/tests/webnn/validation_tests/layerNormalization.https.any.js
@@ -0,0 +1,32 @@
+// META: title=validation tests for WebNN API layerNormalization operation
+// META: global=window,dedicatedworker
+// META: script=../resources/utils_validation.js
+
+'use strict';
+
+const kExampleInputDescriptor = {
+ dataType: 'float32',
+ dimensions: [2, 2]
+};
+
+validateOptionsAxes('layerNormalization', 4);
+
+validateInputFromAnotherBuilder('layerNormalization');
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const scaleFromOtherBuilder =
+ otherBuilder.input('scale', kExampleInputDescriptor);
+ const options = {scale: scaleFromOtherBuilder};
+
+ const input = builder.input('input', kExampleInputDescriptor);
+ assert_throws_js(TypeError, () => builder.layerNormalization(input, options));
+}, '[layerNormalization] throw if scale option is from another builder');
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const biasFromOtherBuilder =
+ otherBuilder.input('bias', kExampleInputDescriptor);
+ const options = {bias: biasFromOtherBuilder};
+
+ const input = builder.input('input', kExampleInputDescriptor);
+ assert_throws_js(TypeError, () => builder.layerNormalization(input, options));
+}, '[layerNormalization] throw if bias option is from another builder');
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
deleted file mode 100644
index 7dbcf5c74a..0000000000
--- a/testing/web-platform/tests/webnn/validation_tests/layer_normalization.https.any.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// 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/leakyRelu.https.any.js b/testing/web-platform/tests/webnn/validation_tests/leakyRelu.https.any.js
new file mode 100644
index 0000000000..6fc19b1f0d
--- /dev/null
+++ b/testing/web-platform/tests/webnn/validation_tests/leakyRelu.https.any.js
@@ -0,0 +1,7 @@
+// META: title=validation tests for WebNN API leakyRelu operation
+// META: global=window,dedicatedworker
+// META: script=../resources/utils_validation.js
+
+'use strict';
+
+validateInputFromAnotherBuilder('leakyRelu');
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
new file mode 100644
index 0000000000..99c1daad3f
--- /dev/null
+++ b/testing/web-platform/tests/webnn/validation_tests/linear.https.any.js
@@ -0,0 +1,7 @@
+// META: title=validation tests for WebNN API linear operation
+// META: global=window,dedicatedworker
+// META: script=../resources/utils_validation.js
+
+'use strict';
+
+validateInputFromAnotherBuilder('linear');
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
index efa05090ca..18d609798c 100644
--- a/testing/web-platform/tests/webnn/validation_tests/lstm.https.any.js
+++ b/testing/web-platform/tests/webnn/validation_tests/lstm.https.any.js
@@ -1,25 +1,56 @@
// 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;
+// Dimensions required of required inputs.
+const kValidInputDimensions = [steps, batchSize, inputSize];
+const kValidWeightDimensions = [numDirections, 4 * hiddenSize, inputSize];
+const kValidRecurrentWeightDimensions =
+ [numDirections, 4 * hiddenSize, hiddenSize];
+// Dimensions required of optional inputs.
+const kValidBiasDimensions = [numDirections, 4 * hiddenSize];
+const kValidPeepholeWeightDimensions = [numDirections, 3 * hiddenSize];
+const kValidInitialHiddenStateDimensions =
+ [numDirections, batchSize, hiddenSize];
+
+// Example descriptors which are valid according to the above dimensions.
+const kExampleInputDescriptor = {
+ dataType: 'float32',
+ dimensions: kValidInputDimensions
+};
+const kExampleWeightDescriptor = {
+ dataType: 'float32',
+ dimensions: kValidWeightDimensions
+};
+const kExampleRecurrentWeightDescriptor = {
+ dataType: 'float32',
+ dimensions: kValidRecurrentWeightDimensions
+};
+const kExampleBiasDescriptor = {
+ dataType: 'float32',
+ dimensions: kValidBiasDimensions
+};
+const kExamplePeepholeWeightDescriptor = {
+ dataType: 'float32',
+ dimensions: kValidPeepholeWeightDimensions
+};
+const kExampleInitialHiddenStateDescriptor = {
+ dataType: 'float32',
+ dimensions: kValidInitialHiddenStateDimensions
+};
+
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]
- },
+ input: {dataType: 'float16', dimensions: kValidInputDimensions},
+ weight: {dataType: 'float16', dimensions: kValidWeightDimensions},
+ recurrentWeight:
+ {dataType: 'float16', dimensions: kValidRecurrentWeightDimensions},
steps: steps,
hiddenSize: hiddenSize,
outputs: [
@@ -29,7 +60,7 @@ const tests = [
},
{
name: '[lstm] Test with given options',
- input: {dataType: 'float32', dimensions: [steps, batchSize, inputSize]},
+ input: kExampleInputDescriptor,
weight: {
dataType: 'float32',
dimensions: [/*numDirections=*/ 2, 4 * hiddenSize, inputSize]
@@ -83,73 +114,43 @@ const tests = [
},
{
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]
- },
+ input: kExampleInputDescriptor,
+ weight: kExampleWeightDescriptor,
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
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]
- },
+ input: kExampleInputDescriptor,
+ weight: kExampleWeightDescriptor,
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
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]
- },
+ input: kExampleInputDescriptor,
+ weight: kExampleWeightDescriptor,
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
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]
- },
+ input: {dataType: 'uint32', dimensions: kValidInputDimensions},
+ weight: {dataType: 'uint32', dimensions: kValidWeightDimensions},
+ recurrentWeight:
+ {dataType: 'uint32', dimensions: kValidRecurrentWeightDimensions},
steps: steps,
hiddenSize: hiddenSize
},
{
- name:
- '[lstm] DataError is expected if the rank of input is not 3',
+ 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]
- },
+ weight: kExampleWeightDescriptor,
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
steps: steps,
hiddenSize: hiddenSize
},
@@ -157,39 +158,27 @@ const tests = [
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]
- },
+ weight: kExampleWeightDescriptor,
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
steps: steps,
hiddenSize: hiddenSize
},
{
name: '[lstm] DataError is expected if the shape of weight is incorrect',
- input: {dataType: 'float32', dimensions: [steps, batchSize, inputSize]},
+ input: kExampleInputDescriptor,
weight: {
dataType: 'float32',
dimensions: [numDirections, 4 * hiddenSize, 1000]
},
- recurrentWeight: {
- dataType: 'float32',
- dimensions: [numDirections, 4 * hiddenSize, hiddenSize]
- },
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
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]
- },
+ input: kExampleInputDescriptor,
+ weight: kExampleWeightDescriptor,
recurrentWeight:
{dataType: 'float32', dimensions: [numDirections, 4 * hiddenSize]},
steps: steps,
@@ -198,31 +187,19 @@ const tests = [
{
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]
- },
+ input: kExampleInputDescriptor,
+ weight: kExampleWeightDescriptor,
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
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]
- },
+ name: '[lstm] DataError is expected if the rank of options.bias is not 2',
+ input: {dataType: 'float16', dimensions: kValidInputDimensions},
+ weight: {dataType: 'float16', dimensions: kValidWeightDimensions},
+ recurrentWeight:
+ {dataType: 'float16', dimensions: kValidRecurrentWeightDimensions},
steps: steps,
hiddenSize: hiddenSize,
options: {bias: {dataType: 'float16', dimensions: [numDirections]}}
@@ -230,15 +207,10 @@ const tests = [
{
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]
- },
+ input: {dataType: 'float16', dimensions: kValidInputDimensions},
+ weight: {dataType: 'float16', dimensions: kValidWeightDimensions},
+ recurrentWeight:
+ {dataType: 'float16', dimensions: kValidRecurrentWeightDimensions},
steps: steps,
hiddenSize: hiddenSize,
options: {
@@ -248,15 +220,10 @@ const tests = [
{
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]
- },
+ input: {dataType: 'float16', dimensions: kValidInputDimensions},
+ weight: {dataType: 'float16', dimensions: kValidWeightDimensions},
+ recurrentWeight:
+ {dataType: 'float16', dimensions: kValidRecurrentWeightDimensions},
steps: steps,
hiddenSize: hiddenSize,
options: {
@@ -267,15 +234,10 @@ const tests = [
{
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]
- },
+ input: {dataType: 'float16', dimensions: kValidInputDimensions},
+ weight: {dataType: 'float16', dimensions: kValidWeightDimensions},
+ recurrentWeight:
+ {dataType: 'float16', dimensions: kValidRecurrentWeightDimensions},
steps: steps,
hiddenSize: hiddenSize,
options: {
@@ -288,15 +250,9 @@ const tests = [
{
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]
- },
+ input: kExampleInputDescriptor,
+ weight: kExampleWeightDescriptor,
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
steps: steps,
hiddenSize: hiddenSize,
options: {
@@ -384,3 +340,132 @@ tests.forEach(
options));
}
}, test.name));
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const inputFromOtherBuilder =
+ otherBuilder.input('input', kExampleInputDescriptor);
+ const weight = builder.input('weight', kExampleWeightDescriptor);
+ const recurrentWeight =
+ builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor);
+
+ assert_throws_js(
+ TypeError,
+ () => builder.lstm(
+ inputFromOtherBuilder, weight, recurrentWeight, steps, hiddenSize));
+}, '[lstm] throw if input is from another builder');
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const input = builder.input('input', kExampleInputDescriptor);
+ const weightFromOtherBuilder =
+ otherBuilder.input('weight', kExampleWeightDescriptor);
+ const recurrentWeight =
+ builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor);
+
+ assert_throws_js(
+ TypeError,
+ () => builder.lstm(
+ input, weightFromOtherBuilder, recurrentWeight, steps, hiddenSize));
+}, '[lstm] throw if weight is from another builder');
+
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const input = builder.input('input', kExampleInputDescriptor);
+ const weight = builder.input('weight', kExampleWeightDescriptor);
+ const recurrentWeightFromOtherBuilder =
+ otherBuilder.input('recurrentWeight', kExampleRecurrentWeightDescriptor);
+
+ assert_throws_js(
+ TypeError,
+ () => builder.lstm(
+ input, weight, recurrentWeightFromOtherBuilder, steps, hiddenSize));
+}, '[lstm] throw if recurrentWeight is from another builder');
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const biasFromOtherBuilder =
+ otherBuilder.input('bias', kExampleBiasDescriptor);
+ const options = {bias: biasFromOtherBuilder};
+
+ const input = builder.input('input', kExampleInputDescriptor);
+ const weight = builder.input('weight', kExampleWeightDescriptor);
+ const recurrentWeight =
+ builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor);
+ assert_throws_js(
+ TypeError,
+ () => builder.lstm(
+ input, weight, recurrentWeight, steps, hiddenSize, options));
+}, '[lstm] throw if bias option is from another builder');
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const recurrentBiasFromOtherBuilder =
+ otherBuilder.input('bias', kExampleBiasDescriptor);
+ const options = {recurrentBias: recurrentBiasFromOtherBuilder};
+
+ const input = builder.input('input', kExampleInputDescriptor);
+ const weight = builder.input('weight', kExampleWeightDescriptor);
+ const recurrentWeight =
+ builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor);
+ assert_throws_js(
+ TypeError,
+ () => builder.lstm(
+ input, weight, recurrentWeight, steps, hiddenSize, options));
+}, '[lstm] throw if recurrentBias option is from another builder');
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const peepholeWeightFromOtherBuilder =
+ otherBuilder.input('peepholeWeight', kExamplePeepholeWeightDescriptor);
+ const options = {peepholeWeight: peepholeWeightFromOtherBuilder};
+
+ const input = builder.input('input', kExampleInputDescriptor);
+ const weight = builder.input('weight', kExampleWeightDescriptor);
+ const recurrentWeight =
+ builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor);
+ assert_throws_js(
+ TypeError,
+ () => builder.lstm(
+ input, weight, recurrentWeight, steps, hiddenSize, options));
+}, '[lstm] throw if peepholeWeight option is from another builder');
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const initialHiddenStateFromOtherBuilder = otherBuilder.input(
+ 'initialHiddenState', kExampleInitialHiddenStateDescriptor);
+ const options = {initialHiddenState: initialHiddenStateFromOtherBuilder};
+
+ const input = builder.input('input', kExampleInputDescriptor);
+ const weight = builder.input('weight', kExampleWeightDescriptor);
+ const recurrentWeight =
+ builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor);
+ assert_throws_js(
+ TypeError,
+ () => builder.lstm(
+ input, weight, recurrentWeight, steps, hiddenSize, options));
+}, '[lstm] throw if initialHiddenState option is from another builder');
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const initialCellStateFromOtherBuilder = otherBuilder.input(
+ 'initialCellState', kExampleInitialHiddenStateDescriptor);
+ const options = {initialCellState: initialCellStateFromOtherBuilder};
+
+ const input = builder.input('input', kExampleInputDescriptor);
+ const weight = builder.input('weight', kExampleWeightDescriptor);
+ const recurrentWeight =
+ builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor);
+ assert_throws_js(
+ TypeError,
+ () => builder.lstm(
+ input, weight, recurrentWeight, steps, hiddenSize, options));
+}, '[lstm] throw if initialCellState option is from another builder');
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const activation = builder.clamp();
+ const activationFromOtherBuilder = otherBuilder.clamp();
+ const options = {activations: [activation, activationFromOtherBuilder]};
+
+ const input = builder.input('input', kExampleInputDescriptor);
+ const weight = builder.input('weight', kExampleWeightDescriptor);
+ const recurrentWeight =
+ builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor);
+ assert_throws_js(
+ TypeError,
+ () => builder.lstm(
+ input, weight, recurrentWeight, steps, hiddenSize, options));
+}, '[lstm] throw if any activation option is from another builder');
diff --git a/testing/web-platform/tests/webnn/validation_tests/lstmCell.https.any.js b/testing/web-platform/tests/webnn/validation_tests/lstmCell.https.any.js
new file mode 100644
index 0000000000..c3769c828d
--- /dev/null
+++ b/testing/web-platform/tests/webnn/validation_tests/lstmCell.https.any.js
@@ -0,0 +1,600 @@
+// META: title=validation tests for WebNN API lstmCell operation
+// META: global=window,dedicatedworker
+// META: script=../resources/utils_validation.js
+
+'use strict';
+
+const batchSize = 3, inputSize = 4, hiddenSize = 5;
+
+// Dimensions required of required inputs.
+const kValidInputDimensions = [batchSize, inputSize];
+const kValidWeightDimensions = [4 * hiddenSize, inputSize];
+const kValidRecurrentWeightDimensions = [4 * hiddenSize, hiddenSize];
+const kValidHiddenStateDimensions = [batchSize, hiddenSize];
+const kValidCellStateDimensions = [batchSize, hiddenSize];
+// Dimensions required of optional inputs.
+const kValidBiasDimensions = [4 * hiddenSize];
+const kValidPeepholeWeightDimensions = [3 * hiddenSize];
+
+// Example descriptors which are valid according to the above dimensions.
+const kExampleInputDescriptor = {
+ dataType: 'float32',
+ dimensions: kValidInputDimensions
+};
+const kExampleWeightDescriptor = {
+ dataType: 'float32',
+ dimensions: kValidWeightDimensions
+};
+const kExampleRecurrentWeightDescriptor = {
+ dataType: 'float32',
+ dimensions: kValidRecurrentWeightDimensions
+};
+const kExampleHiddenStateDescriptor = {
+ dataType: 'float32',
+ dimensions: kValidHiddenStateDimensions
+};
+const kExampleCellStateDescriptor = {
+ dataType: 'float32',
+ dimensions: kValidCellStateDimensions
+};
+const kExampleBiasDescriptor = {
+ dataType: 'float32',
+ dimensions: kValidBiasDimensions
+};
+const kExamplePeepholeWeightDescriptor = {
+ dataType: 'float32',
+ dimensions: kValidPeepholeWeightDimensions
+};
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const inputFromOtherBuilder =
+ otherBuilder.input('input', kExampleInputDescriptor);
+
+ const weight = builder.input('weight', kExampleWeightDescriptor);
+ const recurrentWeight =
+ builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor);
+ const hiddenState =
+ builder.input('hiddenState', kExampleHiddenStateDescriptor);
+ const cellState = builder.input('cellState', kExampleCellStateDescriptor);
+ assert_throws_js(
+ TypeError,
+ () => builder.lstmCell(
+ inputFromOtherBuilder, weight, recurrentWeight, hiddenState,
+ cellState, hiddenSize));
+}, '[lstmCell] throw if input is from another builder');
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const weightFromOtherBuilder =
+ otherBuilder.input('weight', kExampleWeightDescriptor);
+
+ const input = builder.input('input', kExampleInputDescriptor);
+ const recurrentWeight =
+ builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor);
+ const hiddenState =
+ builder.input('hiddenState', kExampleHiddenStateDescriptor);
+ const cellState = builder.input('cellState', kExampleCellStateDescriptor);
+ assert_throws_js(
+ TypeError,
+ () => builder.lstmCell(
+ input, weightFromOtherBuilder, recurrentWeight, hiddenState,
+ cellState, hiddenSize));
+}, '[lstmCell] throw if weight is from another builder');
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const recurrentWeightFromOtherBuilder =
+ otherBuilder.input('recurrentWeight', kExampleRecurrentWeightDescriptor);
+
+ const input = builder.input('input', kExampleInputDescriptor);
+ const weight = builder.input('weight', kExampleWeightDescriptor);
+ const hiddenState =
+ builder.input('hiddenState', kExampleHiddenStateDescriptor);
+ const cellState = builder.input('cellState', kExampleCellStateDescriptor);
+ assert_throws_js(
+ TypeError,
+ () => builder.lstmCell(
+ input, weight, recurrentWeightFromOtherBuilder, hiddenState,
+ cellState, hiddenSize));
+}, '[lstmCell] throw if recurrentWeight is from another builder');
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const hiddenStateFromOtherBuilder =
+ otherBuilder.input('hiddenState', kExampleHiddenStateDescriptor);
+
+ const input = builder.input('input', kExampleInputDescriptor);
+ const weight = builder.input('weight', kExampleWeightDescriptor);
+ const recurrentWeight =
+ builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor);
+ const cellState = builder.input('cellState', kExampleCellStateDescriptor);
+ assert_throws_js(
+ TypeError,
+ () => builder.lstmCell(
+ input, weight, recurrentWeight, hiddenStateFromOtherBuilder,
+ cellState, hiddenSize));
+}, '[lstmCell] throw if hiddenState is from another builder');
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const cellStateFromOtherBuilder =
+ otherBuilder.input('cellState', kExampleCellStateDescriptor);
+
+ const input = builder.input('input', kExampleInputDescriptor);
+ const weight = builder.input('weight', kExampleWeightDescriptor);
+ const recurrentWeight =
+ builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor);
+ const hiddenState =
+ builder.input('hiddenState', kExampleHiddenStateDescriptor);
+ assert_throws_js(
+ TypeError,
+ () => builder.lstmCell(
+ input, weight, recurrentWeight, hiddenState,
+ cellStateFromOtherBuilder, hiddenSize));
+}, '[lstmCell] throw if cellState is from another builder');
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const biasFromOtherBuilder =
+ otherBuilder.input('bias', kExampleBiasDescriptor);
+ const options = {bias: biasFromOtherBuilder};
+
+ const input = builder.input('input', kExampleInputDescriptor);
+ const weight = builder.input('weight', kExampleWeightDescriptor);
+ const recurrentWeight =
+ builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor);
+ const hiddenState =
+ builder.input('hiddenState', kExampleHiddenStateDescriptor);
+ const cellState = builder.input('cellState', kExampleCellStateDescriptor);
+ assert_throws_js(
+ TypeError,
+ () => builder.lstmCell(
+ input, weight, recurrentWeight, hiddenState, cellState, hiddenSize,
+ options));
+}, '[lstmCell] throw if bias option is from another builder');
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const recurrentBiasFromOtherBuilder =
+ otherBuilder.input('bias', kExampleBiasDescriptor);
+ const options = {recurrentBias: recurrentBiasFromOtherBuilder};
+
+ const input = builder.input('input', kExampleInputDescriptor);
+ const weight = builder.input('weight', kExampleWeightDescriptor);
+ const recurrentWeight =
+ builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor);
+ const hiddenState =
+ builder.input('hiddenState', kExampleHiddenStateDescriptor);
+ const cellState = builder.input('cellState', kExampleCellStateDescriptor);
+ assert_throws_js(
+ TypeError,
+ () => builder.lstmCell(
+ input, weight, recurrentWeight, hiddenState, cellState, hiddenSize,
+ options));
+}, '[lstmCell] throw if recurrentBias option is from another builder');
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const peepholeWeightFromOtherBuilder =
+ otherBuilder.input('peepholeWeight', kExamplePeepholeWeightDescriptor);
+ const options = {peepholeWeight: peepholeWeightFromOtherBuilder};
+
+ const input = builder.input('input', kExampleInputDescriptor);
+ const weight = builder.input('weight', kExampleWeightDescriptor);
+ const recurrentWeight =
+ builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor);
+ const hiddenState =
+ builder.input('hiddenState', kExampleHiddenStateDescriptor);
+ const cellState = builder.input('cellState', kExampleCellStateDescriptor);
+ assert_throws_js(
+ TypeError,
+ () => builder.lstmCell(
+ input, weight, recurrentWeight, hiddenState, cellState, hiddenSize,
+ options));
+}, '[lstmCell] throw if peepholeWeight option is from another builder');
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const activation = builder.clamp();
+ const activationFromOtherBuilder = otherBuilder.clamp();
+ const options = {activations: [activation, activationFromOtherBuilder]};
+
+ const input = builder.input('input', kExampleInputDescriptor);
+ const weight = builder.input('weight', kExampleWeightDescriptor);
+ const recurrentWeight =
+ builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor);
+ const hiddenState =
+ builder.input('hiddenState', kExampleHiddenStateDescriptor);
+ const cellState = builder.input('cellState', kExampleCellStateDescriptor);
+ assert_throws_js(
+ TypeError,
+ () => builder.lstmCell(
+ input, weight, recurrentWeight, hiddenState, cellState, hiddenSize,
+ options));
+}, '[lstmCell] throw if activation option is from another builder');
+
+const tests = [
+ {
+ name: '[lstmCell] Test with default options',
+ input: {dataType: 'float16', dimensions: [batchSize, inputSize]},
+ weight: {dataType: 'float16', dimensions: [4 * hiddenSize, inputSize]},
+ recurrentWeight:
+ {dataType: 'float16', dimensions: [4 * hiddenSize, hiddenSize]},
+ hiddenState: {dataType: 'float16', dimensions: [batchSize, hiddenSize]},
+ cellState: {dataType: 'float16', dimensions: [batchSize, hiddenSize]},
+ hiddenSize: hiddenSize,
+ outputs: [
+ {dataType: 'float16', dimensions: [batchSize, hiddenSize]},
+ {dataType: 'float16', dimensions: [batchSize, hiddenSize]}
+ ]
+ },
+ {
+ name: '[lstmCell] Test with given options',
+ input: {dataType: 'float32', dimensions: [batchSize, inputSize]},
+ weight: {dataType: 'float32', dimensions: [4 * hiddenSize, inputSize]},
+ recurrentWeight:
+ {dataType: 'float32', dimensions: [4 * hiddenSize, hiddenSize]},
+ hiddenState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]},
+ cellState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]},
+ hiddenSize: hiddenSize,
+ options: {
+ bias: {dataType: 'float32', dimensions: [4 * hiddenSize]},
+ recurrentBias: {dataType: 'float32', dimensions: [4 * hiddenSize]},
+ peepholeWeight: {dataType: 'float32', dimensions: [3 * hiddenSize]},
+ layout: 'ifgo',
+ activations: ['sigmoid', 'relu', 'tanh']
+ },
+ outputs: [
+ {dataType: 'float32', dimensions: [batchSize, hiddenSize]},
+ {dataType: 'float32', dimensions: [batchSize, hiddenSize]}
+ ]
+ },
+ {
+ name: '[lstmCell] Throw if hiddenSize is equal to zero',
+ input: {dataType: 'float32', dimensions: [batchSize, inputSize]},
+ weight: {dataType: 'float32', dimensions: [4 * hiddenSize, inputSize]},
+ recurrentWeight:
+ {dataType: 'float32', dimensions: [4 * hiddenSize, hiddenSize]},
+ hiddenState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]},
+ cellState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]},
+ hiddenSize: 0
+ },
+ {
+ name: '[lstmCell] Throw if hiddenSize is too large',
+ input: {dataType: 'float32', dimensions: [batchSize, inputSize]},
+ weight: {dataType: 'float32', dimensions: [4 * hiddenSize, inputSize]},
+ recurrentWeight:
+ {dataType: 'float32', dimensions: [4 * hiddenSize, hiddenSize]},
+ hiddenState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]},
+ cellState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]},
+ hiddenSize: 4294967295
+ },
+ {
+ name:
+ '[lstmCell] Throw if the input data type is not one of the floating point types',
+ input: {dataType: 'uint32', dimensions: [batchSize, inputSize]},
+ weight: {dataType: 'float32', dimensions: [4 * hiddenSize, inputSize]},
+ recurrentWeight:
+ {dataType: 'float32', dimensions: [4 * hiddenSize, hiddenSize]},
+ hiddenState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]},
+ cellState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]},
+ hiddenSize: hiddenSize
+ },
+ {
+ name: '[lstmCell] Throw if the rank of input is not 2',
+ input: {dataType: 'float32', dimensions: [batchSize]},
+ weight: {dataType: 'float32', dimensions: [4 * hiddenSize, inputSize]},
+ recurrentWeight:
+ {dataType: 'float32', dimensions: [4 * hiddenSize, hiddenSize]},
+ hiddenState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]},
+ cellState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]},
+ hiddenSize: hiddenSize
+ },
+ {
+ name: '[lstmCell] Throw if the shape of input is incorrect',
+ input: {dataType: 'float32', dimensions: [batchSize, 1000]},
+ weight: {dataType: 'float32', dimensions: [4 * hiddenSize, inputSize]},
+ recurrentWeight:
+ {dataType: 'float32', dimensions: [4 * hiddenSize, hiddenSize]},
+ hiddenState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]},
+ cellState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]},
+ hiddenSize: hiddenSize
+ },
+ {
+ name: '[lstmCell] Throw if the data type of weight is incorrect',
+ input: {dataType: 'float32', dimensions: [batchSize, inputSize]},
+ weight: {dataType: 'float16', dimensions: [4 * hiddenSize, inputSize]},
+ recurrentWeight:
+ {dataType: 'float32', dimensions: [4 * hiddenSize, hiddenSize]},
+ hiddenState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]},
+ cellState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]},
+ hiddenSize: hiddenSize
+ },
+ {
+ name: '[lstmCell] Throw if the rank of weight is not 2',
+ input: {dataType: 'float32', dimensions: [batchSize, inputSize]},
+ weight:
+ {dataType: 'float32', dimensions: [4 * hiddenSize, inputSize, 1000]},
+ recurrentWeight:
+ {dataType: 'float32', dimensions: [4 * hiddenSize, hiddenSize]},
+ hiddenState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]},
+ cellState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]},
+ hiddenSize: hiddenSize
+ },
+ {
+ name: '[lstmCell] Throw if the shape of weight is incorrect',
+ input: {dataType: 'float32', dimensions: [batchSize, inputSize]},
+ weight: {dataType: 'float32', dimensions: [1000, inputSize]},
+ recurrentWeight:
+ {dataType: 'float32', dimensions: [4 * hiddenSize, hiddenSize]},
+ hiddenState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]},
+ cellState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]},
+ hiddenSize: hiddenSize
+ },
+ {
+ name: '[lstmCell] Throw if the data type of recurrentWeight is incorrect',
+ input: {dataType: 'float32', dimensions: [batchSize, inputSize]},
+ weight: {dataType: 'float32', dimensions: [4 * hiddenSize, inputSize]},
+ recurrentWeight:
+ {dataType: 'float16', dimensions: [4 * hiddenSize, hiddenSize]},
+ hiddenState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]},
+ cellState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]},
+ hiddenSize: hiddenSize
+ },
+ {
+ name: '[lstmCell] Throw if the rank of recurrentWeight is not 2',
+ input: {dataType: 'float32', dimensions: [batchSize, inputSize]},
+ weight: {dataType: 'float32', dimensions: [4 * hiddenSize, inputSize]},
+ recurrentWeight:
+ {dataType: 'float32', dimensions: [1000, 4 * hiddenSize, hiddenSize]},
+ hiddenState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]},
+ cellState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]},
+ hiddenSize: hiddenSize
+ },
+ {
+ name: '[lstmCell] Throw if the shape of recurrentWeight is incorrect',
+ input: {dataType: 'float32', dimensions: [batchSize, inputSize]},
+ weight: {dataType: 'float32', dimensions: [4 * hiddenSize, inputSize]},
+ recurrentWeight: {dataType: 'float32', dimensions: [1000, hiddenSize]},
+ hiddenState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]},
+ cellState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]},
+ hiddenSize: hiddenSize
+ },
+ {
+ name: '[lstmCell] Throw if the data type of hiddenState is incorrect',
+ input: {dataType: 'float16', dimensions: [batchSize, inputSize]},
+ weight: {dataType: 'float16', dimensions: [4 * hiddenSize, inputSize]},
+ recurrentWeight:
+ {dataType: 'float16', dimensions: [4 * hiddenSize, hiddenSize]},
+ hiddenState: {dataType: 'int64', dimensions: [batchSize, hiddenSize]},
+ cellState: {dataType: 'float16', dimensions: [batchSize, hiddenSize]},
+ hiddenSize: hiddenSize
+ },
+ {
+ name: '[lstmCell] Throw if the rank of hiddenState is not 2',
+ input: {dataType: 'float32', dimensions: [batchSize, inputSize]},
+ weight: {dataType: 'float32', dimensions: [4 * hiddenSize, inputSize]},
+ recurrentWeight:
+ {dataType: 'float32', dimensions: [4 * hiddenSize, hiddenSize]},
+ hiddenState: {dataType: 'float32', dimensions: [batchSize]},
+ cellState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]},
+ hiddenSize: hiddenSize
+ },
+ {
+ name: '[lstmCell] Throw if the shape of hiddenState is incorrect',
+ input: {dataType: 'float32', dimensions: [batchSize, inputSize]},
+ weight: {dataType: 'float32', dimensions: [4 * hiddenSize, inputSize]},
+ recurrentWeight:
+ {dataType: 'float32', dimensions: [4 * hiddenSize, hiddenSize]},
+ hiddenState: {dataType: 'float32', dimensions: [batchSize, 1000]},
+ cellState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]},
+ hiddenSize: hiddenSize
+ },
+ {
+ name: '[lstmCell] Throw if the data type of cellState is incorrect',
+ input: {dataType: 'float16', dimensions: [batchSize, inputSize]},
+ weight: {dataType: 'float16', dimensions: [4 * hiddenSize, inputSize]},
+ recurrentWeight:
+ {dataType: 'float16', dimensions: [4 * hiddenSize, hiddenSize]},
+ hiddenState: {dataType: 'float16', dimensions: [batchSize, hiddenSize]},
+ cellState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]},
+ hiddenSize: hiddenSize
+ },
+ {
+ name: '[lstmCell] Throw if the rank of cellState is not 2',
+ input: {dataType: 'float32', dimensions: [batchSize, inputSize]},
+ weight: {dataType: 'float32', dimensions: [4 * hiddenSize, inputSize]},
+ recurrentWeight:
+ {dataType: 'float32', dimensions: [4 * hiddenSize, hiddenSize]},
+ hiddenState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]},
+ cellState: {dataType: 'float32', dimensions: [batchSize]},
+ hiddenSize: hiddenSize
+ },
+ {
+ name: '[lstmCell] Throw if the shape of cellState is incorrect',
+ input: {dataType: 'float16', dimensions: [batchSize, inputSize]},
+ weight: {dataType: 'float16', dimensions: [4 * hiddenSize, inputSize]},
+ recurrentWeight:
+ {dataType: 'float16', dimensions: [4 * hiddenSize, hiddenSize]},
+ hiddenState: {dataType: 'float16', dimensions: [batchSize, hiddenSize]},
+ cellState: {dataType: 'float16', dimensions: [batchSize, 1000]},
+ hiddenSize: hiddenSize
+ },
+ {
+ name: '[lstmCell] Throw if the data type of options.bias is incorrect',
+ input: {dataType: 'float16', dimensions: [batchSize, inputSize]},
+ weight: {dataType: 'float16', dimensions: [4 * hiddenSize, inputSize]},
+ recurrentWeight:
+ {dataType: 'float16', dimensions: [4 * hiddenSize, hiddenSize]},
+ hiddenState: {dataType: 'float16', dimensions: [batchSize, hiddenSize]},
+ cellState: {dataType: 'float16', dimensions: [batchSize, hiddenSize]},
+ hiddenSize: hiddenSize,
+ options: {bias: {dataType: 'int8', dimensions: [4 * hiddenSize]}}
+ },
+ {
+ name: '[lstmCell] Throw if the rank of options.bias is not 1',
+ input: {dataType: 'float16', dimensions: [batchSize, inputSize]},
+ weight: {dataType: 'float16', dimensions: [4 * hiddenSize, inputSize]},
+ recurrentWeight:
+ {dataType: 'float16', dimensions: [4 * hiddenSize, hiddenSize]},
+ hiddenState: {dataType: 'float16', dimensions: [batchSize, hiddenSize]},
+ cellState: {dataType: 'float16', dimensions: [batchSize, hiddenSize]},
+ hiddenSize: hiddenSize,
+ options: {bias: {dataType: 'float16', dimensions: [4 * hiddenSize, 1000]}}
+ },
+ {
+ name: '[lstmCell] Throw if the shape of options.bias is incorrect',
+ input: {dataType: 'float16', dimensions: [batchSize, inputSize]},
+ weight: {dataType: 'float16', dimensions: [4 * hiddenSize, inputSize]},
+ recurrentWeight:
+ {dataType: 'float16', dimensions: [4 * hiddenSize, hiddenSize]},
+ hiddenState: {dataType: 'float16', dimensions: [batchSize, hiddenSize]},
+ cellState: {dataType: 'float16', dimensions: [batchSize, hiddenSize]},
+ hiddenSize: hiddenSize,
+ options: {bias: {dataType: 'float16', dimensions: [1000]}}
+ },
+ {
+ name:
+ '[lstmCell] Throw if the data type of options.recurrentBias is incorrect',
+ input: {dataType: 'float16', dimensions: [batchSize, inputSize]},
+ weight: {dataType: 'float16', dimensions: [4 * hiddenSize, inputSize]},
+ recurrentWeight:
+ {dataType: 'float16', dimensions: [4 * hiddenSize, hiddenSize]},
+ hiddenState: {dataType: 'float16', dimensions: [batchSize, hiddenSize]},
+ cellState: {dataType: 'float16', dimensions: [batchSize, hiddenSize]},
+ hiddenSize: hiddenSize,
+ options: {recurrentBias: {dataType: 'uint8', dimensions: [4 * hiddenSize]}}
+ },
+ {
+ name: '[lstmCell] Throw if the rank of options.recurrentBias is not 1',
+ input: {dataType: 'float16', dimensions: [batchSize, inputSize]},
+ weight: {dataType: 'float16', dimensions: [4 * hiddenSize, inputSize]},
+ recurrentWeight:
+ {dataType: 'float16', dimensions: [4 * hiddenSize, hiddenSize]},
+ hiddenState: {dataType: 'float16', dimensions: [batchSize, hiddenSize]},
+ cellState: {dataType: 'float16', dimensions: [batchSize, hiddenSize]},
+ hiddenSize: hiddenSize,
+ options: {
+ recurrentBias: {dataType: 'float16', dimensions: [4 * hiddenSize, 1000]}
+ }
+ },
+ {
+ name: '[lstmCell] Throw if the shape of options.recurrentBias is incorrect',
+ input: {dataType: 'float16', dimensions: [batchSize, inputSize]},
+ weight: {dataType: 'float16', dimensions: [4 * hiddenSize, inputSize]},
+ recurrentWeight:
+ {dataType: 'float16', dimensions: [4 * hiddenSize, hiddenSize]},
+ hiddenState: {dataType: 'float16', dimensions: [batchSize, hiddenSize]},
+ cellState: {dataType: 'float16', dimensions: [batchSize, hiddenSize]},
+ hiddenSize: hiddenSize,
+ options: {recurrentBias: {dataType: 'float16', dimensions: [1000]}}
+ },
+ {
+ name:
+ '[lstmCell] Throw if the data type of options.peepholeWeight is incorrect',
+ input: {dataType: 'float16', dimensions: [batchSize, inputSize]},
+ weight: {dataType: 'float16', dimensions: [4 * hiddenSize, inputSize]},
+ recurrentWeight:
+ {dataType: 'float16', dimensions: [4 * hiddenSize, hiddenSize]},
+ hiddenState: {dataType: 'float16', dimensions: [batchSize, hiddenSize]},
+ cellState: {dataType: 'float16', dimensions: [batchSize, hiddenSize]},
+ hiddenSize: hiddenSize,
+ options:
+ {peepholeWeight: {dataType: 'float32', dimensions: [3 * hiddenSize]}}
+ },
+ {
+ name: '[lstmCell] Throw if the rank of options.peepholeWeight is not 1',
+ input: {dataType: 'float16', dimensions: [batchSize, inputSize]},
+ weight: {dataType: 'float16', dimensions: [4 * hiddenSize, inputSize]},
+ recurrentWeight:
+ {dataType: 'float16', dimensions: [4 * hiddenSize, hiddenSize]},
+ hiddenState: {dataType: 'float16', dimensions: [batchSize, hiddenSize]},
+ cellState: {dataType: 'float16', dimensions: [batchSize, hiddenSize]},
+ hiddenSize: hiddenSize,
+ options: {peepholeWeight: {dataType: 'float16', dimensions: []}}
+ },
+ {
+ name:
+ '[lstmCell] Throw if the shape of options.peepholeWeight is incorrect',
+ input: {dataType: 'float16', dimensions: [batchSize, inputSize]},
+ weight: {dataType: 'float16', dimensions: [4 * hiddenSize, inputSize]},
+ recurrentWeight:
+ {dataType: 'float16', dimensions: [4 * hiddenSize, hiddenSize]},
+ hiddenState: {dataType: 'float16', dimensions: [batchSize, hiddenSize]},
+ cellState: {dataType: 'float16', dimensions: [batchSize, hiddenSize]},
+ hiddenSize: hiddenSize,
+ options: {peepholeWeight: {dataType: 'float16', dimensions: [1000]}}
+ },
+ {
+ name: '[lstmCell] Throw if the size of options.activations is not 3',
+ input: {dataType: 'float32', dimensions: [batchSize, inputSize]},
+ weight: {dataType: 'float32', dimensions: [4 * hiddenSize, inputSize]},
+ recurrentWeight:
+ {dataType: 'float32', dimensions: [4 * hiddenSize, hiddenSize]},
+ hiddenState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]},
+ cellState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]},
+ hiddenSize: hiddenSize,
+ options: {activations: ['sigmoid', 'tanh', 'sigmoid', 'tanh']}
+ }
+];
+
+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 hiddenState = builder.input('hiddenState', {
+ dataType: test.hiddenState.dataType,
+ dimensions: test.hiddenState.dimensions
+ });
+ const cellState = builder.input('cellState', {
+ dataType: test.cellState.dataType,
+ dimensions: test.cellState.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.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.lstmCell(
+ input, weight, recurrentWeight, hiddenState, cellState,
+ 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.lstmCell(
+ input, weight, recurrentWeight, hiddenState, cellState,
+ test.hiddenSize, options));
+ }
+ }, test.name));
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
new file mode 100644
index 0000000000..03616ddb01
--- /dev/null
+++ b/testing/web-platform/tests/webnn/validation_tests/matmul.https.any.js
@@ -0,0 +1,7 @@
+// META: title=validation tests for WebNN API matmul operation
+// META: global=window,dedicatedworker
+// META: script=../resources/utils_validation.js
+
+'use strict';
+
+validateTwoInputsFromMultipleBuilders('matmul');
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
new file mode 100644
index 0000000000..11c6a8f7ef
--- /dev/null
+++ b/testing/web-platform/tests/webnn/validation_tests/pad.https.any.js
@@ -0,0 +1,17 @@
+// META: title=validation tests for WebNN API pad operation
+// META: global=window,dedicatedworker
+// META: script=../resources/utils_validation.js
+
+'use strict';
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const inputFromOtherBuilder =
+ otherBuilder.input('input', {dataType: 'float32', dimensions: [2, 2]});
+
+ const beginningPadding = [1, 1];
+ const endingPadding = [1, 1];
+ assert_throws_js(
+ TypeError,
+ () =>
+ builder.pad(inputFromOtherBuilder, beginningPadding, endingPadding));
+}, '[pad] throw if input is from another builder');
diff --git a/testing/web-platform/tests/webnn/validation_tests/pooling.https.any.js b/testing/web-platform/tests/webnn/validation_tests/pooling.https.any.js
new file mode 100644
index 0000000000..e8add0511f
--- /dev/null
+++ b/testing/web-platform/tests/webnn/validation_tests/pooling.https.any.js
@@ -0,0 +1,275 @@
+// META: title=validation tests for WebNN API pooling operation
+// META: global=window,dedicatedworker
+// META: script=../resources/utils_validation.js
+
+'use strict';
+
+const kPoolingOperators = ['averagePool2d', 'l2Pool2d', 'maxPool2d'];
+
+kPoolingOperators.forEach((operatorName) => {
+ validateInputFromAnotherBuilder(
+ operatorName, {dataType: 'float32', dimensions: [2, 2, 2, 2]});
+});
+
+
+const tests = [
+ {
+ name: 'Test pool2d with default options.',
+ input: {dataType: 'float32', dimensions: [1, 3, 4, 4]},
+ output: {dataType: 'float32', dimensions: [1, 3, 1, 1]}
+ },
+ {
+ name: 'Test pool2d with windowDimensions',
+ input: {dataType: 'float32', dimensions: [1, 3, 4, 4]},
+ options: {
+ windowDimensions: [3, 3],
+ },
+ output: {dataType: 'float32', dimensions: [1, 3, 2, 2]}
+ },
+ {
+ name: 'Test pool2d with padding.',
+ input: {dataType: 'float32', dimensions: [1, 3, 5, 5]},
+ options: {
+ windowDimensions: [5, 5],
+ padding: [2, 2, 2, 2],
+ },
+ output: {dataType: 'float32', dimensions: [1, 3, 5, 5]}
+ },
+ {
+ name: 'Test pool2d with strides.',
+ input: {dataType: 'float32', dimensions: [1, 3, 5, 5]},
+ options: {
+ windowDimensions: [2, 2],
+ strides: [2, 2],
+ },
+ output: {dataType: 'float32', dimensions: [1, 3, 2, 2]}
+ },
+ {
+ name: 'Test pool2d with strides and padding.',
+ input: {dataType: 'float32', dimensions: [1, 3, 5, 5]},
+ options: {
+ windowDimensions: [3, 3],
+ padding: [1, 1, 1, 1],
+ strides: [2, 2],
+ },
+ output: {dataType: 'float32', dimensions: [1, 3, 3, 3]}
+ },
+ {
+ name: 'Test pool2d with strides and asymmetric padding.',
+ input: {dataType: 'float32', dimensions: [1, 3, 7, 7]},
+ options: {
+ windowDimensions: [4, 4],
+ padding: [2, 1, 2, 1],
+ strides: [2, 2],
+ },
+ output: {dataType: 'float32', dimensions: [1, 3, 4, 4]}
+ },
+ {
+ name: 'Test pool2d with strides, padding and roundingType="floor".',
+ input: {dataType: 'float32', dimensions: [1, 3, 7, 7]},
+ options: {
+ windowDimensions: [4, 4],
+ padding: [1, 1, 1, 1],
+ strides: [2, 2],
+ roundingType: 'floor',
+ },
+ output: {dataType: 'float32', dimensions: [1, 3, 3, 3]}
+ },
+ {
+ name: 'Test pool2d with strides, padding and roundingType="ceil".',
+ input: {dataType: 'float32', dimensions: [1, 3, 7, 7]},
+ options: {
+ windowDimensions: [4, 4],
+ padding: [1, 1, 1, 1],
+ strides: [2, 2],
+ roundingType: 'ceil',
+ },
+ output: {dataType: 'float32', dimensions: [1, 3, 4, 4]}
+ },
+ {
+ name: 'Test pool2d with explicit outputSizes ignored roundingType',
+ input: {dataType: 'float32', dimensions: [1, 3, 7, 7]},
+ options: {
+ windowDimensions: [4, 4],
+ padding: [1, 1, 1, 1],
+ strides: [2, 2],
+ roundingType: 'ceil',
+ outputSizes: [3, 3],
+ },
+ output: {dataType: 'float32', dimensions: [1, 3, 3, 3]}
+ },
+ {
+ name: 'Test pool2d with strides, padding and outputSizes=[3, 3].',
+ input: {dataType: 'float32', dimensions: [1, 3, 7, 7]},
+ options: {
+ windowDimensions: [4, 4],
+ padding: [1, 1, 1, 1],
+ strides: [2, 2],
+ outputSizes: [3, 3],
+ },
+ output: {dataType: 'float32', dimensions: [1, 3, 3, 3]}
+ },
+ {
+ name: 'Test pool2d with strides, padding and outputSizes=[4, 4].',
+ input: {dataType: 'float32', dimensions: [1, 3, 7, 7]},
+ options: {
+ windowDimensions: [4, 4],
+ padding: [1, 1, 1, 1],
+ strides: [2, 2],
+ outputSizes: [4, 4],
+ },
+ output: {dataType: 'float32', dimensions: [1, 3, 4, 4]}
+ },
+ {
+ name: 'Test pool2d with layout="nchw".',
+ input: {dataType: 'float32', dimensions: [1, 2, 5, 5]},
+ options: {
+ windowDimensions: [3, 3],
+ layout: 'nchw',
+ },
+ output: {dataType: 'float32', dimensions: [1, 2, 3, 3]}
+ },
+ {
+ name: 'Test pool2d with layout="nhwc".',
+ input: {dataType: 'float32', dimensions: [1, 5, 5, 2]},
+ options: {
+ windowDimensions: [3, 3],
+ layout: 'nhwc',
+ },
+ output: {dataType: 'float32', dimensions: [1, 3, 3, 2]}
+ },
+ {
+ name: 'Throw if the input is not a 4-D tensor.',
+ input: {dataType: 'float32', dimensions: [1, 5, 5]},
+ },
+ {
+ name: 'Throw if the output sizes is incorrect.',
+ input: {dataType: 'float32', dimensions: [1, 2, 5, 5]},
+ options: {
+ windowDimensions: [2, 2],
+ padding: [2, 2, 2, 2],
+ strides: [2, 2],
+ outputSizes: [3, 3],
+ },
+ },
+ {
+ name: 'Throw if the length of output sizes is not 2.',
+ input: {dataType: 'float32', dimensions: [1, 2, 5, 5]},
+ options: {
+ windowDimensions: [2, 2],
+ padding: [2, 2, 2, 2],
+ strides: [2, 2],
+ outputSizes: [1, 2, 4, 4],
+ },
+ },
+ {
+ name: 'Throw if the length of window dimensions is not 2.',
+ input: {dataType: 'float32', dimensions: [1, 2, 5, 5]},
+ options: {
+ windowDimensions: [1, 1, 1, 1],
+ },
+ },
+ {
+ name: 'Throw if any window dimension is lesser than 1.',
+ input: {dataType: 'float32', dimensions: [1, 2, 5, 5]},
+ options: {
+ windowDimensions: [0, 2],
+ },
+ },
+ {
+ name:
+ 'Throw if the input height is too small to fill the pool window height.',
+ input: {dataType: 'float32', dimensions: [1, 2, 5, 5]},
+ options: {
+ windowDimensions: [8, 2],
+ },
+ },
+ {
+ name:
+ 'Throw if the input width is too small to fill the pool window width.',
+ input: {dataType: 'float32', dimensions: [1, 2, 5, 5]},
+ options: {
+ windowDimensions: [2, 8],
+ },
+ },
+ {
+ name: 'Throw if the calculated output height is equal to 0.',
+ input: {dataType: 'float32', dimensions: [1, 2, 5, 5]},
+ options: {
+ windowDimensions: [6, 3],
+ },
+ },
+ {
+ name: 'Throw if the calculated output width is equal to 0.',
+ input: {dataType: 'float32', dimensions: [1, 2, 5, 5]},
+ options: {
+ windowDimensions: [3, 6],
+ },
+ },
+ {
+ name: 'Throw if the length of padding is not 4.',
+ input: {dataType: 'float32', dimensions: [1, 2, 5, 5]},
+ options: {
+ padding: [2, 2],
+ },
+ },
+ {
+ name: 'Throw if the length of strides is not 2.',
+ input: {dataType: 'float32', dimensions: [1, 2, 5, 5]},
+ options: {
+ strides: [2],
+ },
+ },
+ {
+ name: 'Throw if one stride value is smaller than 1.',
+ input: {dataType: 'float32', dimensions: [1, 2, 5, 5]},
+ options: {
+ strides: [0, 2],
+ },
+ },
+ {
+ name: 'Throw if the length of dilations is not 2.',
+ input: {dataType: 'float32', dimensions: [1, 2, 5, 5]},
+ options: {
+ dilations: [1, 1, 2],
+ },
+ },
+ {
+ name: 'Throw if one dilation value is smaller than 1.',
+ input: {dataType: 'float32', dimensions: [1, 2, 5, 5]},
+ options: {
+ dilations: [1, 0],
+ },
+ },
+ {
+ name: 'Throw if the padding height value is too large',
+ input: {dataType: 'float32', dimensions: [1, 3, 5, 5]},
+ options: {
+ padding: [kMaxUnsignedLong, kMaxUnsignedLong, 0, 0],
+ },
+ },
+ {
+ name: 'Throw if the padding width value is too large',
+ input: {dataType: 'float32', dimensions: [1, 3, 5, 5]},
+ options: {
+ padding: [0, 0, kMaxUnsignedLong, kMaxUnsignedLong],
+ },
+ },
+];
+
+tests.forEach(
+ test => promise_test(async t => {
+ const input = builder.input(
+ 'input',
+ {dataType: test.input.dataType, dimensions: test.input.dimensions});
+ kPoolingOperators.forEach((operatorName) => {
+ if (test.output) {
+ const output = builder[operatorName](input, test.options);
+ assert_equals(output.dataType(), test.output.dataType);
+ assert_array_equals(output.shape(), test.output.dimensions);
+ } else {
+ assert_throws_js(
+ TypeError, () => builder[operatorName](input, test.options));
+ }
+ });
+ }, test.name));
diff --git a/testing/web-platform/tests/webnn/validation_tests/prelu.https.any.js b/testing/web-platform/tests/webnn/validation_tests/prelu.https.any.js
new file mode 100644
index 0000000000..865f9f684c
--- /dev/null
+++ b/testing/web-platform/tests/webnn/validation_tests/prelu.https.any.js
@@ -0,0 +1,7 @@
+// META: title=validation tests for WebNN API prelu operation
+// META: global=window,dedicatedworker
+// META: script=../resources/utils_validation.js
+
+'use strict';
+
+validateTwoInputsFromMultipleBuilders('prelu');
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
index 65b71239b9..60f0978678 100644
--- a/testing/web-platform/tests/webnn/validation_tests/reduction.https.any.js
+++ b/testing/web-platform/tests/webnn/validation_tests/reduction.https.any.js
@@ -1,11 +1,10 @@
-// META: title=validation tests for WebNN API reduction operation
+// META: title=validation tests for WebNN API reduction operation
// META: global=window,dedicatedworker
// META: script=../resources/utils_validation.js
-// META: timeout=long
'use strict';
-[
+const kReductionOperators = [
'reduceL1',
'reduceL2',
'reduceLogSum',
@@ -16,6 +15,9 @@
'reduceProduct',
'reduceSum',
'reduceSumSquare',
-].forEach((operationName) => {
- validateOptionsAxes(operationName);
+];
+
+kReductionOperators.forEach((operatorName) => {
+ validateOptionsAxes(operatorName);
+ validateInputFromAnotherBuilder(operatorName);
});
diff --git a/testing/web-platform/tests/webnn/validation_tests/relu.https.any.js b/testing/web-platform/tests/webnn/validation_tests/relu.https.any.js
new file mode 100644
index 0000000000..237c1c3eda
--- /dev/null
+++ b/testing/web-platform/tests/webnn/validation_tests/relu.https.any.js
@@ -0,0 +1,10 @@
+// META: title=validation tests for WebNN API relu operation
+// META: global=window,dedicatedworker
+// META: script=../resources/utils_validation.js
+
+'use strict';
+
+validateInputFromAnotherBuilder('relu');
+
+validateUnaryOperation(
+ 'relu', allWebNNOperandDataTypes, /*alsoBuildActivation=*/ true);
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
index 2e00cf297c..de44c6a333 100644
--- a/testing/web-platform/tests/webnn/validation_tests/resample2d.https.any.js
+++ b/testing/web-platform/tests/webnn/validation_tests/resample2d.https.any.js
@@ -1,8 +1,158 @@
// 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);
+// Tests for resample2d(input, options)
+const tests = [
+ {
+ name: '[resample2d] Test building resample2d with default options',
+ input: {dataType: 'float32', dimensions: [1, 1, 2, 4]},
+ output: {dataType: 'float32', dimensions: [1, 1, 2, 4]},
+ },
+ {
+ name: '[resample2d] Test building resample2d with scales=[2.0, 2.0]',
+ input: {dataType: 'float32', dimensions: [1, 1, 2, 4]},
+ options: {scales: [2.0, 2.0]},
+ output: {dataType: 'float32', dimensions: [1, 1, 4, 8]},
+ },
+ {
+ name: '[resample2d] Test building resample2d with scales=[0.5, 0.5]',
+ input: {dataType: 'float32', dimensions: [1, 1, 5, 5]},
+ options: {scales: [0.5, 0.5]},
+ output: {dataType: 'float32', dimensions: [1, 1, 2, 2]},
+ },
+ {
+ name:
+ '[resample2d] Test building resample2d with scales=[0.5, 0.5] and explicit axes=[2, 3]',
+ input: {dataType: 'float32', dimensions: [1, 1, 5, 5]},
+ options: {scales: [0.5, 0.5], axes: [2, 3]},
+ output: {dataType: 'float32', dimensions: [1, 1, 2, 2]},
+ },
+ {
+ name:
+ '[resample2d] Test building resample2d with scales=[1.0, 2.0] and axes=[0, 1]',
+ input: {dataType: 'float32', dimensions: [1, 1, 2, 4]},
+ options: {scales: [1.0, 2.0], axes: [0, 1]},
+ output: {dataType: 'float32', dimensions: [1, 2, 2, 4]},
+ },
+ {
+ name:
+ '[resample2d] Test building resample2d with scales=[2.0, 2.0] and axes=[1, 2]',
+ input: {dataType: 'float32', dimensions: [1, 1, 2, 4]},
+ options: {scales: [2.0, 2.0], axes: [1, 2]},
+ output: {dataType: 'float32', dimensions: [1, 2, 4, 4]},
+ },
+ {
+ name:
+ '[resample2d] Test building resample2d with sizes=[3, 6] ignored scales',
+ input: {dataType: 'float32', dimensions: [1, 1, 2, 4]},
+ options: {scales: [2.0, 2.0], sizes: [3, 6]},
+ output: {dataType: 'float32', dimensions: [1, 1, 3, 6]},
+ },
+ {
+ name: '[resample2d] Throw if the rank of input is not 4',
+ input: {dataType: 'float32', dimensions: [2, 4]},
+ },
+ {
+ name: '[resample2d] Throw if the length of scales is not 2',
+ input: {dataType: 'float32', dimensions: [1, 1, 2, 4]},
+ options: {scales: [1.0, 1.0, 2.0, 2.0]},
+ },
+ {
+ name: '[resample2d] Throw if any scale value is negative',
+ input: {dataType: 'float32', dimensions: [1, 1, 2, 4]},
+ options: {scales: [1.0, -2.0]},
+ },
+ {
+ name: '[resample2d] Throw if any scale value is 0',
+ input: {dataType: 'float32', dimensions: [1, 1, 2, 4]},
+ options: {scales: [0, 2.0]},
+ },
+ {
+ name: '[resample2d] Throw if the length of sizes is not 2',
+ input: {dataType: 'float32', dimensions: [1, 1, 2, 4]},
+ options: {sizes: [1, 1, 4, 6]},
+ },
+ {
+ name:
+ '[resample2d] Throw if any size value is out of \'unsigned long\' value range',
+ input: {dataType: 'float32', dimensions: [1, 1, 2, 4]},
+ options: {sizes: [kMaxUnsignedLong + 1, kMaxUnsignedLong + 1]},
+ },
+ {
+ name:
+ '[resample2d] Throw if outputHeight being floor(scaleHeight*inputHeight) is too large',
+ input: {dataType: 'float32', dimensions: [1, 1, 2, 4]},
+ // The maximum dimension size is kMaxUnsignedLong (2 ** 32 - 1).
+ // Here scaleHeight=kMaxUnsignedLong and inputHeight=2,
+ // so outputHeight being kMaxUnsignedLong*2 > kMaxUnsignedLong .
+ options: {scales: /*[scaleHeight, scaleWidth]*/[kMaxUnsignedLong, 1]},
+ },
+ {
+ name: '[resample2d] Throw if scaleHeight is too small',
+ input: {dataType: 'float32', dimensions: [1, 1, 2, 4]},
+ // Here scaleHeight=0.02 and inputHeight=2,
+ // so outputHeight would be 0.
+ // Link to https://github.com/webmachinelearning/webnn/issues/391.
+ options: {scales: /*[scaleHeight, scaleWidth]*/[0.02, 0.8]},
+ },
+ {
+ name:
+ '[resample2d] Throw if outputWidth being floor(scaleWidth*inputWidth) is too large',
+ input: {dataType: 'float32', dimensions: [1, 1, 4, 2]},
+ // The maximum dimension size is kMaxUnsignedLong (2 ** 32 - 1).
+ // Here scaleWidth=kMaxUnsignedLong and inputWidth=2,
+ // so outputWidth being kMaxUnsignedLong*2 > kMaxUnsignedLong .
+ options: {scales: /*[scaleHeight, scaleWidth]*/[1, kMaxUnsignedLong]},
+ },
+ {
+ name: '[resample2d] Throw if scaleWidth is too small',
+ input: {dataType: 'float32', dimensions: [1, 1, 2, 4]},
+ // Here scaleWidth=0.1 and inputWidth=4,
+ // so outputWidth would be 0.
+ // Link to https://github.com/webmachinelearning/webnn/issues/391.
+ options: {scales: /*[scaleHeight, scaleWidth]*/[0.7, 0.1]},
+ },
+ {
+ name: '[resample2d] Throw if the length of axes is not 2',
+ input: {dataType: 'float32', dimensions: [1, 1, 2, 4]},
+ options: {axes: [0, 1, 2]},
+ },
+ {
+ name:
+ '[resample2d] Throw if any axis value is greater than or equal to the input rank',
+ input: {dataType: 'float32', dimensions: [1, 1, 2, 4]},
+ options: {axes: [3, 4]},
+ },
+ {
+ // The valid values in the axes sequence are [0, 1], [1, 2] or [2, 3]
+ name: '[resample2d] Throw if the values of axes are inconsecutive',
+ input: {dataType: 'float32', dimensions: [1, 1, 2, 4]},
+ options: {axes: [0, 2]},
+ },
+ {
+ name: '[resample2d] Throw if the values of axes are same',
+ input: {dataType: 'float32', dimensions: [1, 1, 2, 4]},
+ options: {axes: [0, 0]},
+ },
+];
+
+tests.forEach(
+ test => promise_test(async t => {
+ const input = builder.input(
+ 'input',
+ {dataType: test.input.dataType, dimensions: test.input.dimensions});
+ const options = test.options ?? {};
+ if (test.output) {
+ const output = builder.resample2d(input, options);
+ assert_equals(output.dataType(), test.output.dataType);
+ assert_array_equals(output.shape(), test.output.dimensions);
+ } else {
+ assert_throws_js(TypeError, () => builder.resample2d(input, options));
+ }
+ }, test.name));
+
+validateInputFromAnotherBuilder(
+ 'resample2d', {dataType: 'float32', dimensions: [2, 2, 2, 2]});
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
new file mode 100644
index 0000000000..435551b716
--- /dev/null
+++ b/testing/web-platform/tests/webnn/validation_tests/reshape.https.any.js
@@ -0,0 +1,14 @@
+// META: title=validation tests for WebNN API reshape operation
+// META: global=window,dedicatedworker
+// META: script=../resources/utils_validation.js
+
+'use strict';
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const inputFromOtherBuilder =
+ otherBuilder.input('input', {dataType: 'float32', dimensions: [1, 2, 3]});
+
+ const newShape = [3, 2, 1];
+ assert_throws_js(
+ TypeError, () => builder.reshape(inputFromOtherBuilder, newShape));
+}, '[reshape] throw if input is from another builder');
diff --git a/testing/web-platform/tests/webnn/validation_tests/sigmoid.https.any.js b/testing/web-platform/tests/webnn/validation_tests/sigmoid.https.any.js
new file mode 100644
index 0000000000..b40ddc3fd4
--- /dev/null
+++ b/testing/web-platform/tests/webnn/validation_tests/sigmoid.https.any.js
@@ -0,0 +1,10 @@
+// META: title=validation tests for WebNN API sigmoid operation
+// META: global=window,dedicatedworker
+// META: script=../resources/utils_validation.js
+
+'use strict';
+
+validateInputFromAnotherBuilder('sigmoid');
+
+validateUnaryOperation(
+ 'sigmoid', floatingPointTypes, /*alsoBuildActivation=*/ true);
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
new file mode 100644
index 0000000000..a45ecd3fcb
--- /dev/null
+++ b/testing/web-platform/tests/webnn/validation_tests/slice.https.any.js
@@ -0,0 +1,15 @@
+// META: title=validation tests for WebNN API slice operation
+// META: global=window,dedicatedworker
+// META: script=../resources/utils_validation.js
+
+'use strict';
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const inputFromOtherBuilder =
+ otherBuilder.input('input', {dataType: 'float32', dimensions: [2, 2]});
+
+ const starts = [1, 1];
+ const sizes = [1, 1];
+ assert_throws_js(
+ TypeError, () => builder.slice(inputFromOtherBuilder, starts, sizes));
+}, '[slice] throw if input is from another builder');
diff --git a/testing/web-platform/tests/webnn/validation_tests/softmax.https.any.js b/testing/web-platform/tests/webnn/validation_tests/softmax.https.any.js
new file mode 100644
index 0000000000..68891b27d8
--- /dev/null
+++ b/testing/web-platform/tests/webnn/validation_tests/softmax.https.any.js
@@ -0,0 +1,7 @@
+// META: title=validation tests for WebNN API softmax operation
+// META: global=window,dedicatedworker
+// META: script=../resources/utils_validation.js
+
+'use strict';
+
+validateInputFromAnotherBuilder('softmax');
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
new file mode 100644
index 0000000000..347dfcd938
--- /dev/null
+++ b/testing/web-platform/tests/webnn/validation_tests/softplus.https.any.js
@@ -0,0 +1,7 @@
+// META: title=validation tests for WebNN API softplus operation
+// META: global=window,dedicatedworker
+// META: script=../resources/utils_validation.js
+
+'use strict';
+
+validateInputFromAnotherBuilder('softplus');
diff --git a/testing/web-platform/tests/webnn/validation_tests/softsign.https.any.js b/testing/web-platform/tests/webnn/validation_tests/softsign.https.any.js
new file mode 100644
index 0000000000..58ec487159
--- /dev/null
+++ b/testing/web-platform/tests/webnn/validation_tests/softsign.https.any.js
@@ -0,0 +1,10 @@
+// META: title=validation tests for WebNN API softsign operation
+// META: global=window,dedicatedworker
+// META: script=../resources/utils_validation.js
+
+'use strict';
+
+validateInputFromAnotherBuilder('softsign');
+
+validateUnaryOperation(
+ 'softsign', 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
new file mode 100644
index 0000000000..38f3126603
--- /dev/null
+++ b/testing/web-platform/tests/webnn/validation_tests/split.https.any.js
@@ -0,0 +1,14 @@
+// META: title=validation tests for WebNN API split operation
+// META: global=window,dedicatedworker
+// META: script=../resources/utils_validation.js
+
+'use strict';
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const inputFromOtherBuilder =
+ otherBuilder.input('input', {dataType: 'float32', dimensions: [4, 4]});
+
+ const splits = 2;
+ assert_throws_js(
+ TypeError, () => builder.split(inputFromOtherBuilder, splits));
+}, '[split] throw if input is from another builder');
diff --git a/testing/web-platform/tests/webnn/validation_tests/tanh.https.any.js b/testing/web-platform/tests/webnn/validation_tests/tanh.https.any.js
new file mode 100644
index 0000000000..4f9de919f6
--- /dev/null
+++ b/testing/web-platform/tests/webnn/validation_tests/tanh.https.any.js
@@ -0,0 +1,10 @@
+// META: title=validation tests for WebNN API tanh operation
+// META: global=window,dedicatedworker
+// META: script=../resources/utils_validation.js
+
+'use strict';
+
+validateInputFromAnotherBuilder('tanh');
+
+validateUnaryOperation(
+ 'tanh', floatingPointTypes, /*alsoBuildActivation=*/ true);
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
new file mode 100644
index 0000000000..9ea5a5dcf8
--- /dev/null
+++ b/testing/web-platform/tests/webnn/validation_tests/transpose.https.any.js
@@ -0,0 +1,7 @@
+// META: title=validation tests for WebNN API transpose operation
+// META: global=window,dedicatedworker
+// META: script=../resources/utils_validation.js
+
+'use strict';
+
+validateInputFromAnotherBuilder('transpose');
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
index 4e4c368f82..ee8958659c 100644
--- a/testing/web-platform/tests/webnn/validation_tests/triangular.https.any.js
+++ b/testing/web-platform/tests/webnn/validation_tests/triangular.https.any.js
@@ -1,7 +1,6 @@
// META: title=validation tests for WebNN API triangular operation
// META: global=window,dedicatedworker
// META: script=../resources/utils_validation.js
-// META: timeout=long
'use strict';
@@ -14,3 +13,5 @@ promise_test(async t => {
}
}
}, "[triangular] DataError is expected if input's rank is less than 2");
+
+validateInputFromAnotherBuilder('triangular');
diff --git a/testing/web-platform/tests/webnn/validation_tests/where.https.any.js b/testing/web-platform/tests/webnn/validation_tests/where.https.any.js
new file mode 100644
index 0000000000..a26fa24931
--- /dev/null
+++ b/testing/web-platform/tests/webnn/validation_tests/where.https.any.js
@@ -0,0 +1,129 @@
+// META: title=validation tests for WebNN API where operation
+// META: global=window,dedicatedworker
+// META: script=../resources/utils_validation.js
+
+'use strict';
+
+const kExampleConditionDescriptor = {
+ dataType: 'uint8',
+ dimensions: [2, 4]
+};
+const kExampleInputDescriptor = {
+ dataType: 'float32',
+ dimensions: [2, 4]
+};
+
+const tests = [
+ {
+ name:
+ '[where] Throw if the condition data type is not uint8.',
+ condition: {dataType: 'float32', dimensions: [2, 4]},
+ input: {dataType: 'float32', dimensions: [2, 4]},
+ other: {dataType: 'float32', dimensions: [2, 4]},
+ },
+ {
+ name:
+ '[where] Throw if the data types of input and other do not match',
+ condition: {dataType: 'uint8', dimensions: [2, 4]},
+ input: {dataType: 'float16', dimensions: [2, 4]},
+ other: {dataType: 'float32', dimensions: [2, 4]},
+ },
+ {
+ name:
+ '[where] Throw if the shapes of input and other are not broadcastable',
+ condition: {dataType: 'uint8', dimensions: [2, 4]},
+ input: {dataType: 'float32', dimensions: [2, 4]},
+ other: {dataType: 'float32', dimensions: [2, 3]},
+ },
+ {
+ name:
+ '[where] Throw if the condition shape is not broadcastable',
+ condition: {dataType: 'uint8', dimensions: [2, 4]},
+ input: {dataType: 'float32', dimensions: [2, 3]},
+ other: {dataType: 'float32', dimensions: [2, 1]},
+ },
+ {
+ name:
+ '[where] Test building where with 2-D condition, 2-D input and 2-D other using broadcast',
+ condition: {dataType: 'uint8', dimensions: [2, 1]},
+ input: {dataType: 'float32', dimensions: [2, 4]},
+ other: {dataType: 'float32', dimensions: [2, 4]},
+ output: {dataType: 'float32', dimensions: [2, 4]},
+ },
+ {
+ name:
+ '[where] Test building where with 2-D condition, 2-D input and 3-D other using broadcast',
+ condition: {dataType: 'uint8', dimensions: [1, 4]},
+ input: {dataType: 'float32', dimensions: [3, 4]},
+ other: {dataType: 'float32', dimensions: [2, 3, 4]},
+ output: {dataType: 'float32', dimensions: [2, 3, 4]},
+ },
+ {
+ name:
+ '[where] Test building where with 3-D condition, 3-D input and 2-D other using broadcast',
+ condition: {dataType: 'uint8', dimensions: [2, 1, 4]},
+ input: {dataType: 'float32', dimensions: [2, 3, 4]},
+ other: {dataType: 'float32', dimensions: [1, 4]},
+ output: {dataType: 'float32', dimensions: [2, 3, 4]},
+ },
+ {
+ name:
+ '[where] Test building where with 4-D condition, 3-D input and 2-D other using broadcast',
+ condition: {dataType: 'uint8', dimensions: [2, 3, 4, 5]},
+ input: {dataType: 'float32', dimensions: [3, 4, 5]},
+ other: {dataType: 'float32', dimensions: [4, 5]},
+ output: {dataType: 'float32', dimensions: [2, 3, 4, 5]},
+ }
+];
+
+tests.forEach(
+ test => promise_test(async t => {
+ const condition = builder.input('condition', {
+ dataType: test.condition.dataType,
+ dimensions: test.condition.dimensions
+ });
+ const input = builder.input(
+ 'input',
+ {dataType: test.input.dataType, dimensions: test.input.dimensions});
+ const other = builder.input(
+ 'other',
+ {dataType: test.other.dataType, dimensions: test.other.dimensions});
+ if (test.output) {
+ const output = builder.where(condition, input, other);
+ assert_equals(output.dataType(), test.output.dataType);
+ assert_array_equals(output.shape(), test.output.dimensions);
+ } else {
+ assert_throws_js(
+ TypeError, () => builder.where(condition, input, other));
+ }
+ }, test.name));
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const conditionFromOtherBuilder =
+ otherBuilder.input('condition', kExampleConditionDescriptor);
+
+ const input = builder.input('input', kExampleInputDescriptor);
+ const other = builder.input('other', kExampleInputDescriptor);
+ assert_throws_js(
+ TypeError, () => builder.where(conditionFromOtherBuilder, input, other));
+}, '[where] throw if condition is from another builder');
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const inputFromOtherBuilder =
+ otherBuilder.input('input', kExampleInputDescriptor);
+
+ const condition = builder.input('condition', kExampleConditionDescriptor);
+ const other = builder.input('other', kExampleInputDescriptor);
+ assert_throws_js(
+ TypeError, () => builder.where(condition, inputFromOtherBuilder, other));
+}, '[where] throw if input is from another builder');
+
+multi_builder_test(async (t, builder, otherBuilder) => {
+ const otherFromOtherBuilder =
+ otherBuilder.input('other', kExampleInputDescriptor);
+
+ const condition = builder.input('condition', kExampleConditionDescriptor);
+ const input = builder.input('input', kExampleInputDescriptor);
+ assert_throws_js(
+ TypeError, () => builder.where(condition, input, otherFromOtherBuilder));
+}, '[where] throw if other is from another builder');