diff --git a/js/web/package-lock.json b/js/web/package-lock.json index 1d3b7f161c287..d37cf6bd90887 100644 --- a/js/web/package-lock.json +++ b/js/web/package-lock.json @@ -18,6 +18,7 @@ }, "devDependencies": { "@chiragrupani/karma-chromium-edge-launcher": "^2.2.2", + "@petamoriken/float16": "^3.8.7", "@types/chai": "^4.3.4", "@types/emscripten": "^1.39.6", "@types/flatbuffers": "^1.10.0", @@ -127,6 +128,12 @@ "node": ">= 8" } }, + "node_modules/@petamoriken/float16": { + "version": "3.8.7", + "resolved": "https://registry.npmjs.org/@petamoriken/float16/-/float16-3.8.7.tgz", + "integrity": "sha512-/Ri4xDDpe12NT6Ex/DRgHzLlobiQXEW/hmG08w1wj/YU7hLemk97c+zHQFp0iZQ9r7YqgLEXZR2sls4HxBf9NA==", + "dev": true + }, "node_modules/@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", @@ -3589,6 +3596,12 @@ "fastq": "^1.6.0" } }, + "@petamoriken/float16": { + "version": "3.8.7", + "resolved": "https://registry.npmjs.org/@petamoriken/float16/-/float16-3.8.7.tgz", + "integrity": "sha512-/Ri4xDDpe12NT6Ex/DRgHzLlobiQXEW/hmG08w1wj/YU7hLemk97c+zHQFp0iZQ9r7YqgLEXZR2sls4HxBf9NA==", + "dev": true + }, "@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", diff --git a/js/web/package.json b/js/web/package.json index 11e18a5ae1705..94dd047915b05 100644 --- a/js/web/package.json +++ b/js/web/package.json @@ -36,6 +36,7 @@ ], "devDependencies": { "@chiragrupani/karma-chromium-edge-launcher": "^2.2.2", + "@petamoriken/float16": "^3.8.7", "@types/chai": "^4.3.4", "@types/emscripten": "^1.39.6", "@types/flatbuffers": "^1.10.0", diff --git a/js/web/test/data/ops/pad_f16.jsonc b/js/web/test/data/ops/pad_f16.jsonc new file mode 100644 index 0000000000000..44c61b8a95382 --- /dev/null +++ b/js/web/test/data/ops/pad_f16.jsonc @@ -0,0 +1,74 @@ +[ + { + "name": "constant 2D float16", + "operator": "Pad", + "opset": { "domain": "", "version": 10 }, + "attributes": [ + { "name": "mode", "data": "constant", "type": "string" }, + { "name": "value", "data": 1.2, "type": "float" }, + { "name": "pads", "data": [3, 2, 2, 3], "type": "ints" } + ], + "cases": [ + { + "name": "[2,2]->[7,7]", + "inputs": [ + { + "data": [1.0, 2.0, 3.0, 4.5], + "dims": [2, 2], + "type": "float16" + } + ], + "outputs": [ + { + "data": [ + 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, + 1.2, 1.2, 1.0, 2.0, 1.2, 1.2, 1.2, 1.2, 1.2, 3.0, 4.5, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, + 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2 + ], + "dims": [7, 7], + "type": "float16" + } + ] + } + ] + }, + { + "name": "constant 2D float16", + "operator": "Pad", + "opset": { "domain": "", "version": 19 }, + "attributes": [{ "name": "mode", "data": "constant", "type": "string" }], + "cases": [ + { + "name": "[2,2]->[7,7]", + "inputs": [ + { + "data": [1.0, 2.0, 3.0, 4.5], + "dims": [2, 2], + "type": "float16" + }, + { + "data": [3, 2, 2, 3], + "dims": [4], + "type": "int64" + }, + { + "data": [1.2], + "dims": [1], + "type": "float16" + } + ], + "outputs": [ + { + "data": [ + 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, + 1.2, 1.2, 1.0, 2.0, 1.2, 1.2, 1.2, 1.2, 1.2, 3.0, 4.5, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, + 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2 + ], + "dims": [7, 7], + "type": "float16" + } + ] + } + ] + } +] diff --git a/js/web/test/op-test-schema.json b/js/web/test/op-test-schema.json index d6eab6a4ba7bc..0a0a691c37022 100644 --- a/js/web/test/op-test-schema.json +++ b/js/web/test/op-test-schema.json @@ -177,6 +177,7 @@ "properties": { "type": { "enum": [ + "float16", "float32", "float64", "int8", @@ -213,6 +214,7 @@ "properties": { "type": { "enum": [ + "float16", "float32", "float64", "int8", @@ -247,6 +249,7 @@ "properties": { "type": { "enum": [ + "float16", "float32", "float64", "int8", @@ -283,6 +286,7 @@ "properties": { "type": { "enum": [ + "float16", "float32", "float64", "int8", diff --git a/js/web/test/test-runner.ts b/js/web/test/test-runner.ts index e1dd7bbe1967b..bc782a18c55f2 100644 --- a/js/web/test/test-runner.ts +++ b/js/web/test/test-runner.ts @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +import {Float16Array as Float16ArrayPolyfill} from '@petamoriken/float16'; import {expect} from 'chai'; import * as ort from 'onnxruntime-common'; import {extname} from 'path'; @@ -391,6 +392,24 @@ export class TensorResultValidator { case 'string': return this.strictEqual(actual.data, expected.data); + case 'float16': { + const actualData = actual.data as Uint16Array; + const actualDataBuffer = actualData.buffer; + const actualDataByteOffset = actualData.byteOffset; + const actualDataLength = actualData.length; + const actualDataFloat32Array = + new Float32Array(new Float16ArrayPolyfill(actualDataBuffer, actualDataByteOffset, actualDataLength)); + + const expectedData = expected.data as Uint16Array; + const expectedDataBuffer = expectedData.buffer; + const expectedDataByteOffset = expectedData.byteOffset; + const expectedDataLength = expectedData.length; + const expectedDataFloat32Array = + new Float32Array(new Float16ArrayPolyfill(expectedDataBuffer, expectedDataByteOffset, expectedDataLength)); + + return this.floatEqual(actualDataFloat32Array, expectedDataFloat32Array); + } + case 'float32': case 'float64': return this.floatEqual( @@ -919,11 +938,14 @@ async function runProtoOpTestcase( const fetches: Record> = {}; testCase.inputs.forEach((input, i) => { if (input.data) { - let data: number[]|BigUint64Array|BigInt64Array = input.data; + let data: number[]|BigUint64Array|BigInt64Array|Uint16Array = input.data; if (input.type === 'uint64') { data = BigUint64Array.from(input.data.map(BigInt)); } else if (input.type === 'int64') { data = BigInt64Array.from(input.data.map(BigInt)); + } else if (input.type === 'float16') { + const dataArr = Float16ArrayPolyfill.from(input.data); + data = new Uint16Array(dataArr.buffer, dataArr.byteOffset, dataArr.byteLength / 2); } feeds[`input_${i}`] = new ort.Tensor(input.type, data, input.dims); } @@ -933,11 +955,14 @@ async function runProtoOpTestcase( const expectedOutputNames: string[] = []; testCase.outputs.forEach((output, i) => { if (output.data) { - let data: number[]|BigUint64Array|BigInt64Array = output.data; + let data: number[]|BigUint64Array|BigInt64Array|Uint16Array = output.data; if (output.type === 'uint64') { data = BigUint64Array.from(output.data.map(BigInt)); } else if (output.type === 'int64') { data = BigInt64Array.from(output.data.map(BigInt)); + } else if (output.type === 'float16') { + const dataArr = Float16ArrayPolyfill.from(output.data); + data = new Uint16Array(dataArr.buffer, dataArr.byteOffset, dataArr.byteLength / 2); } outputs.push(new ort.Tensor(output.type, data, output.dims)); expectedOutputNames.push(`output_${i}`);