chore: update node_modules with new binary files and dependencies

- Add new binary files for nodemon, onnxruntime-web, and xenova/transformers
- Update various JavaScript and TypeScript files in node_modules
- Remove unused files and dependencies
- Add new test fixtures and documentation files
This commit is contained in:
你的名字
2025-07-18 08:41:48 +08:00
parent db8c0adc79
commit 4c2f5c69a1
4655 changed files with 811329 additions and 35112 deletions

View File

@@ -0,0 +1,68 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseBatchNormalizationAttributes = exports.batchNormalization = void 0;
const attribute_with_cache_key_1 = require("../../../attribute-with-cache-key");
const glsl_source_1 = require("../glsl-source");
const types_1 = require("../types");
const batchNormalizationProgramMetadata = {
name: 'BatchNormalization',
inputNames: ['A', 'Scale', 'B', 'Mean', 'Variance'],
inputTypes: [types_1.TextureType.unpacked, types_1.TextureType.unpacked, types_1.TextureType.unpacked, types_1.TextureType.unpacked, types_1.TextureType.unpacked]
};
const batchNormalization = (inferenceHandler, inputs, attributes) => {
validateInputs(inputs);
const output = inferenceHandler.run(Object.assign(Object.assign({}, batchNormalizationProgramMetadata), { cacheHint: attributes.cacheKey, get: () => createBatchNormalizationProgramInfo(inferenceHandler, inputs, attributes) }), inputs);
return [output];
};
exports.batchNormalization = batchNormalization;
const parseBatchNormalizationAttributes = (node) => {
const epsilon = node.attributes.getFloat('epsilon', 1e-5);
const momentum = node.attributes.getFloat('momentum', 0.9);
const spatial = node.attributes.getInt('spatial', 1);
return (0, attribute_with_cache_key_1.createAttributeWithCacheKey)({ epsilon, momentum, spatial });
};
exports.parseBatchNormalizationAttributes = parseBatchNormalizationAttributes;
const createBatchNormalizationProgramInfo = (inferenceHandler, inputs, attributes) => {
const glsl = (0, glsl_source_1.getGlsl)(inferenceHandler.session.backend.glContext.version);
const rank = inputs[0].dims.length;
const [scaleWidth, scaleHeight] = inferenceHandler.calculateTextureWidthAndHeight(inputs[1].dims, types_1.TextureType.unpacked);
const shaderSource = `
float process(int[${rank}] indices) {
vec2 position = offsetToCoords(indices[1], ${scaleWidth}, ${scaleHeight});
float scale = getColorAsFloat(${glsl.texture2D}(Scale, position));
float mean = getColorAsFloat(${glsl.texture2D}(Mean, position));
float variance = getColorAsFloat(${glsl.texture2D}(Variance, position));
float b = getColorAsFloat(${glsl.texture2D}(B, position));
return scale * ( (_A(indices) - mean) / sqrt(variance + float(${attributes.epsilon})) ) + b;
}`;
return Object.assign(Object.assign({}, batchNormalizationProgramMetadata), { output: { dims: inputs[0].dims, type: inputs[0].type, textureType: types_1.TextureType.unpacked }, shaderSource });
};
const validateInputs = (inputs) => {
if (!inputs || inputs.length !== 5) {
throw new Error('BatchNormalization requires 5 inputs.');
}
const X = inputs[0];
const scale = inputs[1];
const B = inputs[2];
const mean = inputs[3];
const var_ = inputs[4];
// input should atleast have three dimensions - N,C,dim1,...,dimn
// other inputs can have only one dimensions
if (X.dims.length < 3 || scale.dims.length !== 1 || B.dims.length !== 1 || mean.dims.length !== 1 ||
var_.dims.length !== 1) {
throw new Error('invalid input shape.');
}
if (scale.dims[0] !== X.dims[1] || B.dims[0] !== X.dims[1] || mean.dims[0] !== X.dims[1] ||
var_.dims[0] !== X.dims[1]) {
throw new Error('invalid input shape.');
}
if ((X.type !== 'float32' && X.type !== 'float64') || (scale.type !== 'float32' && scale.type !== 'float64') ||
(B.type !== 'float32' && B.type !== 'float64') || (mean.type !== 'float32' && mean.type !== 'float64') ||
(var_.type !== 'float32' && var_.type !== 'float64')) {
throw new Error('invalid input tensor types.');
}
};
//# sourceMappingURL=batch-normalization.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"batch-normalization.js","sourceRoot":"","sources":["batch-normalization.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,96 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import {AttributeWithCacheKey, createAttributeWithCacheKey} from '../../../attribute-with-cache-key';
import {Graph} from '../../../graph';
import {OperatorImplementation, OperatorInitialization} from '../../../operators';
import {Tensor} from '../../../tensor';
import {getGlsl} from '../glsl-source';
import {WebGLInferenceHandler} from '../inference-handler';
import {ProgramInfo, TextureType} from '../types';
export interface BatchNormalizationAttributes extends AttributeWithCacheKey {
epsilon: number;
momentum: number;
spatial: number;
}
const batchNormalizationProgramMetadata = {
name: 'BatchNormalization',
inputNames: ['A', 'Scale', 'B', 'Mean', 'Variance'],
inputTypes:
[TextureType.unpacked, TextureType.unpacked, TextureType.unpacked, TextureType.unpacked, TextureType.unpacked]
};
export const batchNormalization: OperatorImplementation<BatchNormalizationAttributes> =
(inferenceHandler: WebGLInferenceHandler, inputs: Tensor[], attributes: BatchNormalizationAttributes): Tensor[] => {
validateInputs(inputs);
const output = inferenceHandler.run(
{
...batchNormalizationProgramMetadata,
cacheHint: attributes.cacheKey,
get: () => createBatchNormalizationProgramInfo(inferenceHandler, inputs, attributes)
},
inputs);
return [output];
};
export const parseBatchNormalizationAttributes: OperatorInitialization<BatchNormalizationAttributes> =
(node: Graph.Node): BatchNormalizationAttributes => {
const epsilon = node.attributes.getFloat('epsilon', 1e-5);
const momentum = node.attributes.getFloat('momentum', 0.9);
const spatial = node.attributes.getInt('spatial', 1);
return createAttributeWithCacheKey({epsilon, momentum, spatial});
};
const createBatchNormalizationProgramInfo =
(inferenceHandler: WebGLInferenceHandler, inputs: Tensor[], attributes: BatchNormalizationAttributes):
ProgramInfo => {
const glsl = getGlsl(inferenceHandler.session.backend.glContext.version);
const rank = inputs[0].dims.length;
const [scaleWidth, scaleHeight] =
inferenceHandler.calculateTextureWidthAndHeight(inputs[1].dims, TextureType.unpacked);
const shaderSource = `
float process(int[${rank}] indices) {
vec2 position = offsetToCoords(indices[1], ${scaleWidth}, ${scaleHeight});
float scale = getColorAsFloat(${glsl.texture2D}(Scale, position));
float mean = getColorAsFloat(${glsl.texture2D}(Mean, position));
float variance = getColorAsFloat(${glsl.texture2D}(Variance, position));
float b = getColorAsFloat(${glsl.texture2D}(B, position));
return scale * ( (_A(indices) - mean) / sqrt(variance + float(${attributes.epsilon})) ) + b;
}`;
return {
...batchNormalizationProgramMetadata,
output: {dims: inputs[0].dims, type: inputs[0].type, textureType: TextureType.unpacked},
shaderSource
};
};
const validateInputs = (inputs: Tensor[]): void => {
if (!inputs || inputs.length !== 5) {
throw new Error('BatchNormalization requires 5 inputs.');
}
const X = inputs[0];
const scale = inputs[1];
const B = inputs[2];
const mean = inputs[3];
const var_ = inputs[4];
// input should atleast have three dimensions - N,C,dim1,...,dimn
// other inputs can have only one dimensions
if (X.dims.length < 3 || scale.dims.length !== 1 || B.dims.length !== 1 || mean.dims.length !== 1 ||
var_.dims.length !== 1) {
throw new Error('invalid input shape.');
}
if (scale.dims[0] !== X.dims[1] || B.dims[0] !== X.dims[1] || mean.dims[0] !== X.dims[1] ||
var_.dims[0] !== X.dims[1]) {
throw new Error('invalid input shape.');
}
if ((X.type !== 'float32' && X.type !== 'float64') || (scale.type !== 'float32' && scale.type !== 'float64') ||
(B.type !== 'float32' && B.type !== 'float64') || (mean.type !== 'float32' && mean.type !== 'float64') ||
(var_.type !== 'float32' && var_.type !== 'float64')) {
throw new Error('invalid input tensor types.');
}
};

View File

@@ -0,0 +1,291 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.xor = exports.sub = exports.pRelu = exports.pow = exports.or = exports.mul = exports.less = exports.greater = exports.equal = exports.div = exports.and = exports.add = exports.glslPRelu = exports.glslPow = exports.glslXor = exports.glslOr = exports.glslAnd = exports.glslLess = exports.glslGreater = exports.glslEqual = exports.glslSub = exports.glslMul = exports.glslDiv = exports.glslAdd = void 0;
const util_1 = require("../../../util");
const glsl_definitions_1 = require("../glsl-definitions");
const glsl_source_1 = require("../glsl-source");
const types_1 = require("../types");
function glslAdd() {
const name = 'add_';
const body = `
float ${name}(float a, float b) {
return a + b;
}
vec4 ${name}(vec4 v1, vec4 v2) {
return v1 + v2;
}
`;
return { body, name, type: glsl_definitions_1.FunctionType.ValueBased };
}
exports.glslAdd = glslAdd;
function glslDiv() {
const name = 'div_';
const body = `
float ${name}(float a, float b) {
return a / b;
}
vec4 ${name}(vec4 v1, vec4 v2) {
return v1 / v2;
}
`;
return { body, name, type: glsl_definitions_1.FunctionType.ValueBased };
}
exports.glslDiv = glslDiv;
function glslMul() {
const name = 'mul_';
const body = `
float ${name}(float a, float b) {
return a * b;
}
vec4 ${name}(vec4 v1, vec4 v2) {
return v1 * v2;
}
`;
return { body, name, type: glsl_definitions_1.FunctionType.ValueBased };
}
exports.glslMul = glslMul;
function glslSub() {
const name = 'sub_';
const body = `
float ${name}(float a, float b) {
return a - b;
}
vec4 ${name}(vec4 v1, vec4 v2) {
return v1 - v2;
}
`;
return { body, name, type: glsl_definitions_1.FunctionType.ValueBased };
}
exports.glslSub = glslSub;
function glslEqual() {
const name = 'equal_';
const body = `
float ${name}(float a, float b) {
return float(a == b);
}
vec4 ${name}(vec4 v1, vec4 v2) {
return vec4(equal(v1, v2));
}
`;
return { body, name, type: glsl_definitions_1.FunctionType.ValueBased };
}
exports.glslEqual = glslEqual;
function glslGreater() {
const name = 'greater_';
const body = `
float ${name}(float a, float b) {
return float(a > b);
}
vec4 ${name}(vec4 v1, vec4 v2) {
return vec4( v1.r > v2.r ,
v1.g > v2.g,
v1.b > v2.b,
v1.a > v2.a );
}
`;
return { body, name, type: glsl_definitions_1.FunctionType.ValueBased };
}
exports.glslGreater = glslGreater;
function glslLess() {
const name = 'less_';
const body = `
float ${name}(float a, float b) {
return float(a < b);
}
vec4 ${name}(vec4 v1, vec4 v2) {
return vec4( v1.r < v2.r ,
v1.g < v2.g,
v1.b < v2.b,
v1.a < v2.a );
}
`;
return { body, name, type: glsl_definitions_1.FunctionType.ValueBased };
}
exports.glslLess = glslLess;
function glslAnd() {
const name = 'and_';
const body = `
float ${name}(float a, float b) {
return float( bool(a) && bool(b) );
}
vec4 ${name}(vec4 v1, vec4 v2) {
bvec4 b1 = bvec4(v1);
bvec4 b2 = bvec4(v2);
return vec4( b1.r && b2.r ,
b1.g && b2.g,
b1.b && b2.b,
b1.a && b2.a );
}
`;
return { body, name, type: glsl_definitions_1.FunctionType.ValueBased };
}
exports.glslAnd = glslAnd;
function glslOr() {
const name = 'or_';
const body = `
float ${name}(float a, float b) {
return float( bool(a) || bool(b) );
}
vec4 ${name}(vec4 v1, vec4 v2) {
bvec4 b1 = bvec4(v1);
bvec4 b2 = bvec4(v2);
return vec4( b1.r || b2.r ,
b1.g || b2.g,
b1.b || b2.b,
b1.a || b2.a );
}
`;
return { body, name, type: glsl_definitions_1.FunctionType.ValueBased };
}
exports.glslOr = glslOr;
function glslXor() {
const name = 'xor_';
const body = `
float ${name}(float a, float b) {
return float( bool(a) ^^ bool(b) );
}
vec4 ${name}(vec4 v1, vec4 v2) {
bvec4 b1 = bvec4(v1);
bvec4 b2 = bvec4(v2);
return vec4( b1.r ^^ b2.r ,
b1.g ^^ b2.g,
b1.b ^^ b2.b,
b1.a ^^ b2.a );
}
`;
return { body, name, type: glsl_definitions_1.FunctionType.ValueBased };
}
exports.glslXor = glslXor;
function glslPow() {
return glslBuiltinBinary('pow');
}
exports.glslPow = glslPow;
function glslPRelu() {
const name = 'prelu_';
const body = `
float ${name}(float a, float b) {
return a < 0.0 ? a * b: a;
}
vec4 ${name}(vec4 v1, vec4 v2) {
return vec4(
v1.r < 0.0 ? v1.r * v2.r: v1.r,
v1.g < 0.0 ? v1.g * v2.g: v1.g,
v1.b < 0.0 ? v1.b * v2.b: v1.b,
v1.a < 0.0 ? v1.a * v2.a: v1.a
);
}
`;
return { body, name, type: glsl_definitions_1.FunctionType.ValueBased };
}
exports.glslPRelu = glslPRelu;
function glslBuiltinBinary(fname) {
const name = `${fname}_`;
const body = `
float ${name}(float a, float b) {
return ${fname}(a, b);
}
vec4 ${name}(vec4 v1, vec4 v2) {
return ${fname}(v1, v2);
}
`;
return { body, name, type: glsl_definitions_1.FunctionType.ValueBased };
}
const createBinaryProgramInfoLoader = (handler, inputs, glslFunc, outputTensorType = inputs[0].type, cacheKey) => {
const textureType = handler.session.pack ? types_1.TextureType.packed : types_1.TextureType.unpacked;
return {
name: glslFunc.name,
inputNames: ['A', 'B'],
inputTypes: [textureType, textureType],
cacheHint: cacheKey,
get: () => createBinaryProgramInfo(handler, inputs, glslFunc, outputTensorType)
};
};
const createBinaryProgramInfo = (handler, inputs, glslFunc, outputTensorType = inputs[0].type) => {
const textureType = handler.session.pack ? types_1.TextureType.packed : types_1.TextureType.unpacked;
const isBroadcast = !util_1.ShapeUtil.areEqual(inputs[0].dims, inputs[1].dims);
let outputShape = inputs[0].dims;
const usePackedTexture = handler.session.pack;
if (isBroadcast) {
const calculatedShape = util_1.BroadcastUtil.calcShape(inputs[0].dims, inputs[1].dims, false);
if (!calculatedShape) {
throw new Error('Can\'t perform binary op on the given tensors');
}
outputShape = calculatedShape;
const outputRank = outputShape.length;
const aRank = inputs[0].dims.length !== 0 ? inputs[0].dims.length : 1;
const bRank = inputs[1].dims.length !== 0 ? inputs[1].dims.length : 1;
const aBcast = inputs[0].dims.length !== 0 ? 'bcastIndices_A(indices, aindices);' : 'aindices[0] = 0;';
const bBcast = inputs[1].dims.length !== 0 ? 'bcastIndices_B(indices, bindices);' : 'bindices[0] = 0;';
const glsl = (0, glsl_source_1.getGlsl)(handler.session.backend.glContext.version);
const shaderSource = usePackedTexture ? `
${glslFunc.body}
void main() {
vec4 a = getAAtOutCoords();
vec4 b = getBAtOutCoords();
vec4 result = ${glslFunc.name}(a, b);
${glsl.output} = result;
}` :
`
${glslFunc.body}
float process(int indices[${outputRank}]) {
int aindices[${aRank}];
int bindices[${bRank}];
${aBcast}
${bBcast}
return ${glslFunc.name}(_A(aindices), _B(bindices));
}`;
return {
name: glslFunc.name,
inputNames: ['A', 'B'],
inputTypes: [textureType, textureType],
output: { dims: outputShape, type: outputTensorType, textureType },
shaderSource,
hasMain: usePackedTexture
};
}
const glsl = (0, glsl_source_1.getGlsl)(handler.session.backend.glContext.version);
const shaderSource = `
${glslFunc.body}
void main() {
vec4 v1 = ${glsl.texture2D}(A, TexCoords);
vec4 v2 = ${glsl.texture2D}(B, TexCoords);
vec4 result = ${glslFunc.name}(v1, v2);
${glsl.output} = result;
}
`;
return {
name: glslFunc.name,
inputNames: ['A', 'B'],
inputTypes: [textureType, textureType],
output: { dims: inputs[0].dims, type: outputTensorType, textureType },
shaderSource,
hasMain: true
};
};
const add = (handler, inputs) => [handler.run(createBinaryProgramInfoLoader(handler, inputs, glslAdd()), inputs)];
exports.add = add;
const and = (handler, inputs) => [handler.run(createBinaryProgramInfoLoader(handler, inputs, glslAnd(), 'bool'), inputs)];
exports.and = and;
const div = (handler, inputs) => [handler.run(createBinaryProgramInfoLoader(handler, inputs, glslDiv()), inputs)];
exports.div = div;
const equal = (handler, inputs) => [handler.run(createBinaryProgramInfoLoader(handler, inputs, glslEqual(), 'bool'), inputs)];
exports.equal = equal;
const greater = (handler, inputs) => [handler.run(createBinaryProgramInfoLoader(handler, inputs, glslGreater(), 'bool'), inputs)];
exports.greater = greater;
const less = (handler, inputs) => [handler.run(createBinaryProgramInfoLoader(handler, inputs, glslLess(), 'bool'), inputs)];
exports.less = less;
const mul = (handler, inputs) => [handler.run(createBinaryProgramInfoLoader(handler, inputs, glslMul()), inputs)];
exports.mul = mul;
const or = (handler, inputs) => [handler.run(createBinaryProgramInfoLoader(handler, inputs, glslOr(), 'bool'), inputs)];
exports.or = or;
const pow = (handler, inputs) => [handler.run(createBinaryProgramInfoLoader(handler, inputs, glslPow()), inputs)];
exports.pow = pow;
const pRelu = (handler, inputs) => [handler.run(createBinaryProgramInfoLoader(handler, inputs, glslPRelu()), inputs)];
exports.pRelu = pRelu;
const sub = (handler, inputs) => [handler.run(createBinaryProgramInfoLoader(handler, inputs, glslSub()), inputs)];
exports.sub = sub;
const xor = (handler, inputs) => [handler.run(createBinaryProgramInfoLoader(handler, inputs, glslXor(), 'bool'), inputs)];
exports.xor = xor;
//# sourceMappingURL=binary-op.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,303 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import {Tensor} from '../../../tensor';
import {BroadcastUtil, ShapeUtil} from '../../../util';
import {FunctionType, GlslValueFunction} from '../glsl-definitions';
import {getGlsl} from '../glsl-source';
import {WebGLInferenceHandler} from '../inference-handler';
import {ProgramInfo, ProgramInfoLoader, TextureType} from '../types';
export function glslAdd(): GlslValueFunction {
const name = 'add_';
const body = `
float ${name}(float a, float b) {
return a + b;
}
vec4 ${name}(vec4 v1, vec4 v2) {
return v1 + v2;
}
`;
return {body, name, type: FunctionType.ValueBased};
}
export function glslDiv(): GlslValueFunction {
const name = 'div_';
const body = `
float ${name}(float a, float b) {
return a / b;
}
vec4 ${name}(vec4 v1, vec4 v2) {
return v1 / v2;
}
`;
return {body, name, type: FunctionType.ValueBased};
}
export function glslMul(): GlslValueFunction {
const name = 'mul_';
const body = `
float ${name}(float a, float b) {
return a * b;
}
vec4 ${name}(vec4 v1, vec4 v2) {
return v1 * v2;
}
`;
return {body, name, type: FunctionType.ValueBased};
}
export function glslSub(): GlslValueFunction {
const name = 'sub_';
const body = `
float ${name}(float a, float b) {
return a - b;
}
vec4 ${name}(vec4 v1, vec4 v2) {
return v1 - v2;
}
`;
return {body, name, type: FunctionType.ValueBased};
}
export function glslEqual(): GlslValueFunction {
const name = 'equal_';
const body = `
float ${name}(float a, float b) {
return float(a == b);
}
vec4 ${name}(vec4 v1, vec4 v2) {
return vec4(equal(v1, v2));
}
`;
return {body, name, type: FunctionType.ValueBased};
}
export function glslGreater(): GlslValueFunction {
const name = 'greater_';
const body = `
float ${name}(float a, float b) {
return float(a > b);
}
vec4 ${name}(vec4 v1, vec4 v2) {
return vec4( v1.r > v2.r ,
v1.g > v2.g,
v1.b > v2.b,
v1.a > v2.a );
}
`;
return {body, name, type: FunctionType.ValueBased};
}
export function glslLess(): GlslValueFunction {
const name = 'less_';
const body = `
float ${name}(float a, float b) {
return float(a < b);
}
vec4 ${name}(vec4 v1, vec4 v2) {
return vec4( v1.r < v2.r ,
v1.g < v2.g,
v1.b < v2.b,
v1.a < v2.a );
}
`;
return {body, name, type: FunctionType.ValueBased};
}
export function glslAnd(): GlslValueFunction {
const name = 'and_';
const body = `
float ${name}(float a, float b) {
return float( bool(a) && bool(b) );
}
vec4 ${name}(vec4 v1, vec4 v2) {
bvec4 b1 = bvec4(v1);
bvec4 b2 = bvec4(v2);
return vec4( b1.r && b2.r ,
b1.g && b2.g,
b1.b && b2.b,
b1.a && b2.a );
}
`;
return {body, name, type: FunctionType.ValueBased};
}
export function glslOr(): GlslValueFunction {
const name = 'or_';
const body = `
float ${name}(float a, float b) {
return float( bool(a) || bool(b) );
}
vec4 ${name}(vec4 v1, vec4 v2) {
bvec4 b1 = bvec4(v1);
bvec4 b2 = bvec4(v2);
return vec4( b1.r || b2.r ,
b1.g || b2.g,
b1.b || b2.b,
b1.a || b2.a );
}
`;
return {body, name, type: FunctionType.ValueBased};
}
export function glslXor(): GlslValueFunction {
const name = 'xor_';
const body = `
float ${name}(float a, float b) {
return float( bool(a) ^^ bool(b) );
}
vec4 ${name}(vec4 v1, vec4 v2) {
bvec4 b1 = bvec4(v1);
bvec4 b2 = bvec4(v2);
return vec4( b1.r ^^ b2.r ,
b1.g ^^ b2.g,
b1.b ^^ b2.b,
b1.a ^^ b2.a );
}
`;
return {body, name, type: FunctionType.ValueBased};
}
export function glslPow(): GlslValueFunction {
return glslBuiltinBinary('pow');
}
export function glslPRelu(): GlslValueFunction {
const name = 'prelu_';
const body = `
float ${name}(float a, float b) {
return a < 0.0 ? a * b: a;
}
vec4 ${name}(vec4 v1, vec4 v2) {
return vec4(
v1.r < 0.0 ? v1.r * v2.r: v1.r,
v1.g < 0.0 ? v1.g * v2.g: v1.g,
v1.b < 0.0 ? v1.b * v2.b: v1.b,
v1.a < 0.0 ? v1.a * v2.a: v1.a
);
}
`;
return {body, name, type: FunctionType.ValueBased};
}
function glslBuiltinBinary(fname: string): GlslValueFunction {
const name = `${fname}_`;
const body = `
float ${name}(float a, float b) {
return ${fname}(a, b);
}
vec4 ${name}(vec4 v1, vec4 v2) {
return ${fname}(v1, v2);
}
`;
return {body, name, type: FunctionType.ValueBased};
}
const createBinaryProgramInfoLoader =
(handler: WebGLInferenceHandler, inputs: Tensor[], glslFunc: GlslValueFunction,
outputTensorType: Tensor.DataType = inputs[0].type, cacheKey?: string): ProgramInfoLoader => {
const textureType = handler.session.pack ? TextureType.packed : TextureType.unpacked;
return {
name: glslFunc.name,
inputNames: ['A', 'B'],
inputTypes: [textureType, textureType],
cacheHint: cacheKey,
get: () => createBinaryProgramInfo(handler, inputs, glslFunc, outputTensorType)
};
};
const createBinaryProgramInfo =
(handler: WebGLInferenceHandler, inputs: Tensor[], glslFunc: GlslValueFunction,
outputTensorType: Tensor.DataType = inputs[0].type): ProgramInfo => {
const textureType = handler.session.pack ? TextureType.packed : TextureType.unpacked;
const isBroadcast = !ShapeUtil.areEqual(inputs[0].dims, inputs[1].dims);
let outputShape = inputs[0].dims;
const usePackedTexture = handler.session.pack;
if (isBroadcast) {
const calculatedShape = BroadcastUtil.calcShape(inputs[0].dims, inputs[1].dims, false);
if (!calculatedShape) {
throw new Error('Can\'t perform binary op on the given tensors');
}
outputShape = calculatedShape;
const outputRank = outputShape.length;
const aRank = inputs[0].dims.length !== 0 ? inputs[0].dims.length : 1;
const bRank = inputs[1].dims.length !== 0 ? inputs[1].dims.length : 1;
const aBcast = inputs[0].dims.length !== 0 ? 'bcastIndices_A(indices, aindices);' : 'aindices[0] = 0;';
const bBcast = inputs[1].dims.length !== 0 ? 'bcastIndices_B(indices, bindices);' : 'bindices[0] = 0;';
const glsl = getGlsl(handler.session.backend.glContext.version);
const shaderSource = usePackedTexture ? `
${glslFunc.body}
void main() {
vec4 a = getAAtOutCoords();
vec4 b = getBAtOutCoords();
vec4 result = ${glslFunc.name}(a, b);
${glsl.output} = result;
}` :
`
${glslFunc.body}
float process(int indices[${outputRank}]) {
int aindices[${aRank}];
int bindices[${bRank}];
${aBcast}
${bBcast}
return ${glslFunc.name}(_A(aindices), _B(bindices));
}`;
return {
name: glslFunc.name,
inputNames: ['A', 'B'],
inputTypes: [textureType, textureType],
output: {dims: outputShape, type: outputTensorType, textureType},
shaderSource,
hasMain: usePackedTexture
};
}
const glsl = getGlsl(handler.session.backend.glContext.version);
const shaderSource = `
${glslFunc.body}
void main() {
vec4 v1 = ${glsl.texture2D}(A, TexCoords);
vec4 v2 = ${glsl.texture2D}(B, TexCoords);
vec4 result = ${glslFunc.name}(v1, v2);
${glsl.output} = result;
}
`;
return {
name: glslFunc.name,
inputNames: ['A', 'B'],
inputTypes: [textureType, textureType],
output: {dims: inputs[0].dims, type: outputTensorType, textureType},
shaderSource,
hasMain: true
};
};
export const add = (handler: WebGLInferenceHandler, inputs: Tensor[]):
Tensor[] => [handler.run(createBinaryProgramInfoLoader(handler, inputs, glslAdd()), inputs)];
export const and = (handler: WebGLInferenceHandler, inputs: Tensor[]):
Tensor[] => [handler.run(createBinaryProgramInfoLoader(handler, inputs, glslAnd(), 'bool'), inputs)];
export const div = (handler: WebGLInferenceHandler, inputs: Tensor[]):
Tensor[] => [handler.run(createBinaryProgramInfoLoader(handler, inputs, glslDiv()), inputs)];
export const equal = (handler: WebGLInferenceHandler, inputs: Tensor[]):
Tensor[] => [handler.run(createBinaryProgramInfoLoader(handler, inputs, glslEqual(), 'bool'), inputs)];
export const greater = (handler: WebGLInferenceHandler, inputs: Tensor[]):
Tensor[] => [handler.run(createBinaryProgramInfoLoader(handler, inputs, glslGreater(), 'bool'), inputs)];
export const less = (handler: WebGLInferenceHandler, inputs: Tensor[]):
Tensor[] => [handler.run(createBinaryProgramInfoLoader(handler, inputs, glslLess(), 'bool'), inputs)];
export const mul = (handler: WebGLInferenceHandler, inputs: Tensor[]):
Tensor[] => [handler.run(createBinaryProgramInfoLoader(handler, inputs, glslMul()), inputs)];
export const or = (handler: WebGLInferenceHandler, inputs: Tensor[]):
Tensor[] => [handler.run(createBinaryProgramInfoLoader(handler, inputs, glslOr(), 'bool'), inputs)];
export const pow = (handler: WebGLInferenceHandler, inputs: Tensor[]):
Tensor[] => [handler.run(createBinaryProgramInfoLoader(handler, inputs, glslPow()), inputs)];
export const pRelu = (handler: WebGLInferenceHandler, inputs: Tensor[]):
Tensor[] => [handler.run(createBinaryProgramInfoLoader(handler, inputs, glslPRelu()), inputs)];
export const sub = (handler: WebGLInferenceHandler, inputs: Tensor[]):
Tensor[] => [handler.run(createBinaryProgramInfoLoader(handler, inputs, glslSub()), inputs)];
export const xor = (handler: WebGLInferenceHandler, inputs: Tensor[]):
Tensor[] => [handler.run(createBinaryProgramInfoLoader(handler, inputs, glslXor(), 'bool'), inputs)];

View File

@@ -0,0 +1,22 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseCastAttributes = exports.cast = void 0;
const util_1 = require("../../../util");
const cast = (handler, inputs, to) => {
validateInputs(inputs);
return [handler.cast(inputs[0], to)];
};
exports.cast = cast;
const parseCastAttributes = (node) => util_1.ProtoUtil.tensorDataTypeFromProto(node.attributes.getInt('to'));
exports.parseCastAttributes = parseCastAttributes;
const validateInputs = (inputs) => {
if (!inputs || inputs.length !== 1) {
throw new Error('Cast requires 1 input.');
}
if (inputs[0].type === 'string') {
throw new Error('Invalid input type.');
}
};
//# sourceMappingURL=cast.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"cast.js","sourceRoot":"","sources":["cast.ts"],"names":[],"mappings":";AAAA,4DAA4D;AAC5D,kCAAkC;;;AAKlC,wCAAwC;AAGjC,MAAM,IAAI,GACb,CAAC,OAA8B,EAAE,MAAgB,EAAE,EAAmB,EAAY,EAAE;IAClF,cAAc,CAAC,MAAM,CAAC,CAAC;IACvB,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;AACvC,CAAC,CAAC;AAJO,QAAA,IAAI,QAIX;AAEC,MAAM,mBAAmB,GAA4C,CAAC,IAAgB,EAAmB,EAAE,CAC9G,gBAAS,CAAC,uBAAuB,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;AADvD,QAAA,mBAAmB,uBACoC;AAEpE,MAAM,cAAc,GAAG,CAAC,MAAgB,EAAQ,EAAE;IAChD,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE;QAClC,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;KAC3C;IAED,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,EAAE;QAC/B,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;KACxC;AACH,CAAC,CAAC"}

View File

@@ -0,0 +1,27 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import {Graph} from '../../../graph';
import {OperatorImplementation, OperatorInitialization} from '../../../operators';
import {Tensor} from '../../../tensor';
import {ProtoUtil} from '../../../util';
import {WebGLInferenceHandler} from '../inference-handler';
export const cast: OperatorImplementation<Tensor.DataType> =
(handler: WebGLInferenceHandler, inputs: Tensor[], to: Tensor.DataType): Tensor[] => {
validateInputs(inputs);
return [handler.cast(inputs[0], to)];
};
export const parseCastAttributes: OperatorInitialization<Tensor.DataType> = (node: Graph.Node): Tensor.DataType =>
ProtoUtil.tensorDataTypeFromProto(node.attributes.getInt('to'));
const validateInputs = (inputs: Tensor[]): void => {
if (!inputs || inputs.length !== 1) {
throw new Error('Cast requires 1 input.');
}
if (inputs[0].type === 'string') {
throw new Error('Invalid input type.');
}
};

View File

@@ -0,0 +1,125 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.createPackedConcatProgramInfoLoader = void 0;
const glsl_source_1 = require("../glsl-source");
const types_1 = require("../types");
const utils_1 = require("../utils");
const packing_utils_1 = require("./packing-utils");
const createPackedConcatProgramMetadata = (inputCount, cacheHint) => ({
name: 'Concat (packed)',
inputNames: Array.from({ length: inputCount }, (v, i) => `X${i}`),
inputTypes: Array(inputCount).fill(types_1.TextureType.packed),
cacheHint
});
const createPackedConcatProgramInfo = (handler, metadata, inputs, axis) => {
const inputShape = inputs[0].dims.slice();
if (axis >= inputShape.length || axis < (-1 * inputShape.length)) {
throw new Error('axis specified for concat doesn\'t match input dimensionality');
}
if (axis < 0) {
axis = inputShape.length + axis;
}
// ensure all of the non-concatenated axes match each other
// calculate the shape of the output tensor while we do that
const outputShape = inputShape.slice(0);
for (let i = 1; i < inputs.length; i++) {
const dataNShape = inputs[i].dims.slice();
for (let axisIndex = 0; axisIndex < inputShape.length; axisIndex++) {
// add to the placeholder for computing output shape
if (axisIndex === axis) {
outputShape[axis] += dataNShape[axisIndex];
}
// ensure all non-cancatenated axes match each other
else if (inputShape[axisIndex] !== dataNShape[axisIndex]) {
throw new Error('non concat dimensions must match');
}
}
}
const rank = outputShape.length;
const coords = (0, packing_utils_1.getChannels)('coords', rank);
const dtype = (0, utils_1.getCoordsDataType)(rank);
const unpackChannel = (0, packing_utils_1.unpackFromChannel)();
const shapes = inputs.map(i => i.dims);
const channels = (0, utils_1.getGlChannels)(rank);
const offsets = new Array(shapes.length - 1);
offsets[0] = shapes[0][axis];
for (let i = 1; i < offsets.length; i++) {
offsets[i] = offsets[i - 1] + shapes[i][axis];
}
const channel = channels[axis];
const lastChannels = channels.slice(-2);
const allChannels = channels.join();
let getValueSnippet = `if (${channel} < ${offsets[0]}) {
return getChannel(
getX0(${allChannels}), vec2(${lastChannels.join()}));
}`;
for (let i = 1; i < offsets.length; i++) {
const shift = offsets[i - 1];
getValueSnippet += `
if (${channel} < ${offsets[i]} && ${channel} >= ${offsets[i - 1]}) {
return getChannel(
getX${i}(${getShiftedChannelsSnippet(channels, channel, shift)}),
vec2(${getShiftedChannelsSnippet(lastChannels, channel, shift)}));
}`;
}
const lastIndex = offsets.length;
const shift = offsets[offsets.length - 1];
getValueSnippet += `
return getChannel(
getX${lastIndex}(${getShiftedChannelsSnippet(channels, channel, shift)}),
vec2(${getShiftedChannelsSnippet(lastChannels, channel, shift)}));`;
const glsl = (0, glsl_source_1.getGlsl)(handler.session.backend.glContext.version);
const shaderSource = `
${unpackChannel}
float getValue(${channels.map(x => 'int ' + x)}) {
${getValueSnippet}
}
void main() {
${dtype} coords = getOutputCoords();
int lastDim = coords.${channels[rank - 1]};
coords.${channels[rank - 1]} = coords.${channels[rank - 2]};
coords.${channels[rank - 2]} = lastDim;
vec4 result = vec4(getValue(${coords}), 0., 0., 0.);
${coords[rank - 1]} = ${coords[rank - 1]} + 1;
if (${coords[rank - 1]} < ${outputShape[rank - 1]}) {
result.g = getValue(${coords});
}
${coords[rank - 2]} = ${coords[rank - 2]} + 1;
if (${coords[rank - 2]} < ${outputShape[rank - 2]}) {
result.a = getValue(${coords});
}
${coords[rank - 1]} = ${coords[rank - 1]} - 1;
if (${coords[rank - 2]} < ${outputShape[rank - 2]} &&
${coords[rank - 1]} < ${outputShape[rank - 1]}) {
result.b = getValue(${coords});
}
${glsl.output} = result;
}
`;
return Object.assign(Object.assign({}, metadata), { output: { dims: outputShape, type: inputs[0].type, textureType: types_1.TextureType.packed }, shaderSource, hasMain: true });
};
const createPackedConcatProgramInfoLoader = (handler, inputs, attributes) => {
const metadata = createPackedConcatProgramMetadata(inputs.length, attributes.cacheKey);
return Object.assign(Object.assign({}, metadata), { get: () => createPackedConcatProgramInfo(handler, metadata, inputs, attributes.axis) });
};
exports.createPackedConcatProgramInfoLoader = createPackedConcatProgramInfoLoader;
const getShiftedChannelsSnippet = (channels, channel, shift) => {
const channelIdx = channels.indexOf(channel);
const res = channels.map((c, idx) => {
if (idx === channelIdx) {
return `${c} - ${shift}`;
}
else {
return c;
}
});
return res.join();
};
//# sourceMappingURL=concat-packed.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"concat-packed.js","sourceRoot":"","sources":["concat-packed.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,143 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import {Tensor} from '../../../tensor';
import {getGlsl} from '../glsl-source';
import {WebGLInferenceHandler} from '../inference-handler';
import {ProgramInfo, ProgramInfoLoader, ProgramMetadata, TextureType} from '../types';
import {getCoordsDataType, getGlChannels} from '../utils';
import {ConcatAttributes} from './concat';
import {getChannels, unpackFromChannel} from './packing-utils';
const createPackedConcatProgramMetadata = (inputCount: number, cacheHint: string) => ({
name: 'Concat (packed)',
inputNames: Array.from({length: inputCount}, (v, i) => `X${i}`),
inputTypes: Array(inputCount).fill(TextureType.packed),
cacheHint
});
const createPackedConcatProgramInfo =
(handler: WebGLInferenceHandler, metadata: ProgramMetadata, inputs: Tensor[], axis: number): ProgramInfo => {
const inputShape = inputs[0].dims.slice();
if (axis >= inputShape.length || axis < (-1 * inputShape.length)) {
throw new Error('axis specified for concat doesn\'t match input dimensionality');
}
if (axis < 0) {
axis = inputShape.length + axis;
}
// ensure all of the non-concatenated axes match each other
// calculate the shape of the output tensor while we do that
const outputShape = inputShape.slice(0);
for (let i = 1; i < inputs.length; i++) {
const dataNShape = inputs[i].dims.slice();
for (let axisIndex = 0; axisIndex < inputShape.length; axisIndex++) {
// add to the placeholder for computing output shape
if (axisIndex === axis) {
outputShape[axis] += dataNShape[axisIndex];
}
// ensure all non-cancatenated axes match each other
else if (inputShape[axisIndex] !== dataNShape[axisIndex]) {
throw new Error('non concat dimensions must match');
}
}
}
const rank = outputShape.length;
const coords = getChannels('coords', rank);
const dtype = getCoordsDataType(rank);
const unpackChannel = unpackFromChannel();
const shapes = inputs.map(i => i.dims);
const channels = getGlChannels(rank);
const offsets: number[] = new Array(shapes.length - 1);
offsets[0] = shapes[0][axis];
for (let i = 1; i < offsets.length; i++) {
offsets[i] = offsets[i - 1] + shapes[i][axis];
}
const channel = channels[axis];
const lastChannels = channels.slice(-2);
const allChannels = channels.join();
let getValueSnippet = `if (${channel} < ${offsets[0]}) {
return getChannel(
getX0(${allChannels}), vec2(${lastChannels.join()}));
}`;
for (let i = 1; i < offsets.length; i++) {
const shift = offsets[i - 1];
getValueSnippet += `
if (${channel} < ${offsets[i]} && ${channel} >= ${offsets[i - 1]}) {
return getChannel(
getX${i}(${getShiftedChannelsSnippet(channels, channel, shift)}),
vec2(${getShiftedChannelsSnippet(lastChannels, channel, shift)}));
}`;
}
const lastIndex = offsets.length;
const shift = offsets[offsets.length - 1];
getValueSnippet += `
return getChannel(
getX${lastIndex}(${getShiftedChannelsSnippet(channels, channel, shift)}),
vec2(${getShiftedChannelsSnippet(lastChannels, channel, shift)}));`;
const glsl = getGlsl(handler.session.backend.glContext.version);
const shaderSource = `
${unpackChannel}
float getValue(${channels.map(x => 'int ' + x)}) {
${getValueSnippet}
}
void main() {
${dtype} coords = getOutputCoords();
int lastDim = coords.${channels[rank - 1]};
coords.${channels[rank - 1]} = coords.${channels[rank - 2]};
coords.${channels[rank - 2]} = lastDim;
vec4 result = vec4(getValue(${coords}), 0., 0., 0.);
${coords[rank - 1]} = ${coords[rank - 1]} + 1;
if (${coords[rank - 1]} < ${outputShape[rank - 1]}) {
result.g = getValue(${coords});
}
${coords[rank - 2]} = ${coords[rank - 2]} + 1;
if (${coords[rank - 2]} < ${outputShape[rank - 2]}) {
result.a = getValue(${coords});
}
${coords[rank - 1]} = ${coords[rank - 1]} - 1;
if (${coords[rank - 2]} < ${outputShape[rank - 2]} &&
${coords[rank - 1]} < ${outputShape[rank - 1]}) {
result.b = getValue(${coords});
}
${glsl.output} = result;
}
`;
return {
...metadata,
output: {dims: outputShape, type: inputs[0].type, textureType: TextureType.packed},
shaderSource,
hasMain: true,
};
};
export const createPackedConcatProgramInfoLoader =
(handler: WebGLInferenceHandler, inputs: Tensor[], attributes: ConcatAttributes): ProgramInfoLoader => {
const metadata = createPackedConcatProgramMetadata(inputs.length, attributes.cacheKey);
return {...metadata, get: () => createPackedConcatProgramInfo(handler, metadata, inputs, attributes.axis)};
};
const getShiftedChannelsSnippet = (channels: string[], channel: string, shift: number): string => {
const channelIdx = channels.indexOf(channel);
const res = channels.map((c, idx) => {
if (idx === channelIdx) {
return `${c} - ${shift}`;
} else {
return c;
}
});
return res.join();
};

View File

@@ -0,0 +1,159 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseConcatAttributes = exports.concat = void 0;
const attribute_with_cache_key_1 = require("../../../attribute-with-cache-key");
const types_1 = require("../types");
const concat_packed_1 = require("./concat-packed");
const concat = (inferenceHandler, inputs, attributes) => {
validateInputs(inputs);
if (inferenceHandler.session.pack && inputs[0].dims.length > 1) {
const output = inferenceHandler.run((0, concat_packed_1.createPackedConcatProgramInfoLoader)(inferenceHandler, inputs, attributes), inputs);
return [output];
}
else {
const output = inferenceHandler.run(createUnpackedConcatProgramInfoLoader(inferenceHandler, inputs, attributes), inputs);
return [output];
}
};
exports.concat = concat;
const createUnpackedConcatProgramMetadata = (inputCount, cacheHint) => ({
name: 'Concat',
inputNames: Array.from({ length: inputCount }, (v, i) => `X${i}`),
inputTypes: Array(inputCount).fill(types_1.TextureType.unpacked),
cacheHint
});
const createUnpackedConcatProgramInfo = (handler, metadata, inputs, axis) => {
const inputShape = inputs[0].dims.slice();
if (axis >= inputShape.length || axis < (-1 * inputShape.length)) {
throw new Error('axis specified for concat doesn\'t match input dimensionality');
}
if (axis < 0) {
axis = inputShape.length + axis;
}
// ensure all of the non-concatenated axes match each other
// calculate the shape of the output tensor while we do that
const outputShape = inputShape.slice(0);
for (let i = 1; i < inputs.length; i++) {
const dataNShape = inputs[i].dims.slice();
for (let axisIndex = 0; axisIndex < inputShape.length; axisIndex++) {
// add to the placeholder for computing output shape
if (axisIndex === axis) {
outputShape[axis] += dataNShape[axisIndex];
}
// ensure all non-cancatenated axes match each other
else if (inputShape[axisIndex] !== dataNShape[axisIndex]) {
throw new Error('non concat dimensions must match');
}
}
}
const rank = outputShape.length;
const sizeInConcatAxis = new Array(inputs.length);
let previousSum = 0;
for (let i = 0; i < sizeInConcatAxis.length; ++i) {
previousSum += inputs[i].dims[axis];
sizeInConcatAxis[i] = previousSum;
}
let getTextureIndexWhereDataResidesMethod = '';
// in most cases linear search is sufficient, as in most scenarios, only 2 tensors are concatenated
if (inputs.length < 5) {
getTextureIndexWhereDataResidesMethod = getTextureIndexWhereDataResidesLinearSearch(sizeInConcatAxis);
}
else {
getTextureIndexWhereDataResidesMethod = getTextureIndexWhereDataResidesBinarySearch(sizeInConcatAxis);
}
const fetchDataFromCorrectTextureMethod = getFetchDataFromCorrectTextureMethod(inputs.length, rank);
const getSizeInConcatAxisValueFromIndexMethod = getGetSizeInConcatAxisValueFromIndexMethod(sizeInConcatAxis);
const shaderSource = `
${fetchDataFromCorrectTextureMethod}
${getSizeInConcatAxisValueFromIndexMethod}
${getTextureIndexWhereDataResidesMethod}
float process(int indices[${rank}]) {
int textureIndex = getTextureWhereDataResides (indices[${axis}]);
if(textureIndex != 0) {
indices[${axis}] = indices[${axis}] - int(getSizeInConcatAxisValueFromIndex(textureIndex-int(1)));
}
return fetchDataFromCorrectTexture(textureIndex, indices);
}`;
return Object.assign(Object.assign({}, metadata), { output: { dims: outputShape, type: inputs[0].type, textureType: types_1.TextureType.unpacked }, shaderSource });
};
const createUnpackedConcatProgramInfoLoader = (handler, inputs, attributes) => {
const metadata = createUnpackedConcatProgramMetadata(inputs.length, attributes.cacheKey);
return Object.assign(Object.assign({}, metadata), { get: () => createUnpackedConcatProgramInfo(handler, metadata, inputs, attributes.axis) });
};
const getTextureIndexWhereDataResidesLinearSearch = (sizeInConcatAxis) => {
const searchAxis = sizeInConcatAxis.map((size, i) => `if(index<${size}) {return ${i};}
`);
return `int getTextureWhereDataResides(int index) {
${searchAxis.join('')}
}`;
};
// TODO: Implement BinarySearch in GLSL
const getTextureIndexWhereDataResidesBinarySearch = (sizeInConcatAxis) => getTextureIndexWhereDataResidesLinearSearch(sizeInConcatAxis);
const getFetchDataFromCorrectTextureMethod = (numberOfTensors, tensorRank) => {
const codeLines = [`float fetchDataFromCorrectTexture(int textureIndex, int indices[${tensorRank}]) {`];
for (let i = 0; i < numberOfTensors; ++i) {
if (i === 0) {
codeLines.push('\t' +
`if (textureIndex == ${i}) { return _X${i}(indices); }`);
}
else if (i === numberOfTensors - 1) {
codeLines.push('\t' +
`else { return _X${i}(indices); }`);
}
else {
codeLines.push('\t' +
`else if (textureIndex == ${i}) { return _X${i}(indices); }`);
}
}
codeLines.push('\t' +
'}');
return codeLines.join('\n');
};
const getGetSizeInConcatAxisValueFromIndexMethod = (sizeInConcatAxis) => {
const codeLines = ['int getSizeInConcatAxisValueFromIndex(int index) {'];
for (let i = 0; i < sizeInConcatAxis.length; ++i) {
if (i === 0) {
codeLines.push('\t' +
`if (index == ${i}) { return ${sizeInConcatAxis[i]}; }`);
}
else if (i === sizeInConcatAxis.length - 1) {
codeLines.push('\t' +
`else { return ${sizeInConcatAxis[i]}; }`);
}
else {
codeLines.push('\t' +
`else if (index == ${i}) { return ${sizeInConcatAxis[i]}; }`);
}
}
codeLines.push('\t' +
'}');
return codeLines.join('\n');
};
const parseConcatAttributes = (node) => (0, attribute_with_cache_key_1.createAttributeWithCacheKey)({ axis: node.attributes.getInt('axis') });
exports.parseConcatAttributes = parseConcatAttributes;
const validateInputs = (inputs) => {
if (!inputs || inputs.length < 1) {
throw new Error('too few inputs');
}
const inputType = inputs[0].type;
const inputDimensionality = inputs[0].dims.length;
// TODO: Support string concat
if (inputType === 'string') {
throw new Error('string tensor is not supported yet');
}
for (const input of inputs) {
// make sure types of all inputs match
if (input.type !== inputType) {
throw new Error('input tensors should be one type');
}
// make sure the dimensionality of all inputs are the same
if (input.dims.length !== inputDimensionality) {
throw new Error('input tensors should have the same shape');
}
}
};
//# sourceMappingURL=concat.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,195 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import {AttributeWithCacheKey, createAttributeWithCacheKey} from '../../../attribute-with-cache-key';
import {Graph} from '../../../graph';
import {OperatorImplementation, OperatorInitialization} from '../../../operators';
import {Tensor} from '../../../tensor';
import {WebGLInferenceHandler} from '../inference-handler';
import {ProgramInfo, ProgramInfoLoader, ProgramMetadata, TextureType} from '../types';
import {createPackedConcatProgramInfoLoader} from './concat-packed';
export interface ConcatAttributes extends AttributeWithCacheKey {
readonly axis: number;
}
export const concat: OperatorImplementation<ConcatAttributes> =
(inferenceHandler: WebGLInferenceHandler, inputs: Tensor[], attributes: ConcatAttributes): Tensor[] => {
validateInputs(inputs);
if (inferenceHandler.session.pack && inputs[0].dims.length > 1) {
const output =
inferenceHandler.run(createPackedConcatProgramInfoLoader(inferenceHandler, inputs, attributes), inputs);
return [output];
} else {
const output =
inferenceHandler.run(createUnpackedConcatProgramInfoLoader(inferenceHandler, inputs, attributes), inputs);
return [output];
}
};
const createUnpackedConcatProgramMetadata = (inputCount: number, cacheHint: string) => ({
name: 'Concat',
inputNames: Array.from({length: inputCount}, (v, i) => `X${i}`),
inputTypes: Array(inputCount).fill(TextureType.unpacked),
cacheHint
});
const createUnpackedConcatProgramInfo =
(handler: WebGLInferenceHandler, metadata: ProgramMetadata, inputs: Tensor[], axis: number): ProgramInfo => {
const inputShape = inputs[0].dims.slice();
if (axis >= inputShape.length || axis < (-1 * inputShape.length)) {
throw new Error('axis specified for concat doesn\'t match input dimensionality');
}
if (axis < 0) {
axis = inputShape.length + axis;
}
// ensure all of the non-concatenated axes match each other
// calculate the shape of the output tensor while we do that
const outputShape = inputShape.slice(0);
for (let i = 1; i < inputs.length; i++) {
const dataNShape = inputs[i].dims.slice();
for (let axisIndex = 0; axisIndex < inputShape.length; axisIndex++) {
// add to the placeholder for computing output shape
if (axisIndex === axis) {
outputShape[axis] += dataNShape[axisIndex];
}
// ensure all non-cancatenated axes match each other
else if (inputShape[axisIndex] !== dataNShape[axisIndex]) {
throw new Error('non concat dimensions must match');
}
}
}
const rank = outputShape.length;
const sizeInConcatAxis = new Array<number>(inputs.length);
let previousSum = 0;
for (let i = 0; i < sizeInConcatAxis.length; ++i) {
previousSum += inputs[i].dims[axis];
sizeInConcatAxis[i] = previousSum;
}
let getTextureIndexWhereDataResidesMethod = '';
// in most cases linear search is sufficient, as in most scenarios, only 2 tensors are concatenated
if (inputs.length < 5) {
getTextureIndexWhereDataResidesMethod = getTextureIndexWhereDataResidesLinearSearch(sizeInConcatAxis);
} else {
getTextureIndexWhereDataResidesMethod = getTextureIndexWhereDataResidesBinarySearch(sizeInConcatAxis);
}
const fetchDataFromCorrectTextureMethod = getFetchDataFromCorrectTextureMethod(inputs.length, rank);
const getSizeInConcatAxisValueFromIndexMethod = getGetSizeInConcatAxisValueFromIndexMethod(sizeInConcatAxis);
const shaderSource = `
${fetchDataFromCorrectTextureMethod}
${getSizeInConcatAxisValueFromIndexMethod}
${getTextureIndexWhereDataResidesMethod}
float process(int indices[${rank}]) {
int textureIndex = getTextureWhereDataResides (indices[${axis}]);
if(textureIndex != 0) {
indices[${axis}] = indices[${axis}] - int(getSizeInConcatAxisValueFromIndex(textureIndex-int(1)));
}
return fetchDataFromCorrectTexture(textureIndex, indices);
}`;
return {
...metadata,
output: {dims: outputShape, type: inputs[0].type, textureType: TextureType.unpacked},
shaderSource,
};
};
const createUnpackedConcatProgramInfoLoader =
(handler: WebGLInferenceHandler, inputs: Tensor[], attributes: ConcatAttributes): ProgramInfoLoader => {
const metadata = createUnpackedConcatProgramMetadata(inputs.length, attributes.cacheKey);
return {...metadata, get: () => createUnpackedConcatProgramInfo(handler, metadata, inputs, attributes.axis)};
};
const getTextureIndexWhereDataResidesLinearSearch = (sizeInConcatAxis: number[]): string => {
const searchAxis = sizeInConcatAxis.map((size, i) => `if(index<${size}) {return ${i};}
`);
return `int getTextureWhereDataResides(int index) {
${searchAxis.join('')}
}`;
};
// TODO: Implement BinarySearch in GLSL
const getTextureIndexWhereDataResidesBinarySearch = (sizeInConcatAxis: number[]): string =>
getTextureIndexWhereDataResidesLinearSearch(sizeInConcatAxis);
const getFetchDataFromCorrectTextureMethod = (numberOfTensors: number, tensorRank: number) => {
const codeLines: string[] = [`float fetchDataFromCorrectTexture(int textureIndex, int indices[${tensorRank}]) {`];
for (let i = 0; i < numberOfTensors; ++i) {
if (i === 0) {
codeLines.push(
'\t' +
`if (textureIndex == ${i}) { return _X${i}(indices); }`);
} else if (i === numberOfTensors - 1) {
codeLines.push(
'\t' +
`else { return _X${i}(indices); }`);
} else {
codeLines.push(
'\t' +
`else if (textureIndex == ${i}) { return _X${i}(indices); }`);
}
}
codeLines.push(
'\t' +
'}');
return codeLines.join('\n');
};
const getGetSizeInConcatAxisValueFromIndexMethod = (sizeInConcatAxis: number[]): string => {
const codeLines: string[] = ['int getSizeInConcatAxisValueFromIndex(int index) {'];
for (let i = 0; i < sizeInConcatAxis.length; ++i) {
if (i === 0) {
codeLines.push(
'\t' +
`if (index == ${i}) { return ${sizeInConcatAxis[i]}; }`);
} else if (i === sizeInConcatAxis.length - 1) {
codeLines.push(
'\t' +
`else { return ${sizeInConcatAxis[i]}; }`);
} else {
codeLines.push(
'\t' +
`else if (index == ${i}) { return ${sizeInConcatAxis[i]}; }`);
}
}
codeLines.push(
'\t' +
'}');
return codeLines.join('\n');
};
export const parseConcatAttributes: OperatorInitialization<ConcatAttributes> = (node: Graph.Node): ConcatAttributes =>
createAttributeWithCacheKey({axis: node.attributes.getInt('axis')});
const validateInputs = (inputs: Tensor[]): void => {
if (!inputs || inputs.length < 1) {
throw new Error('too few inputs');
}
const inputType = inputs[0].type;
const inputDimensionality = inputs[0].dims.length;
// TODO: Support string concat
if (inputType === 'string') {
throw new Error('string tensor is not supported yet');
}
for (const input of inputs) {
// make sure types of all inputs match
if (input.type !== inputType) {
throw new Error('input tensors should be one type');
}
// make sure the dimensionality of all inputs are the same
if (input.dims.length !== inputDimensionality) {
throw new Error('input tensors should have the same shape');
}
}
};

View File

@@ -0,0 +1,73 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.createUnpackedGroupedConvProgramInfoLoader = void 0;
const instrument_1 = require("../../../instrument");
const glsl_source_1 = require("../glsl-source");
const types_1 = require("../types");
const conv_1 = require("./conv");
const fuse_utils_1 = require("./fuse-utils");
const createUnpackedGroupedConvProgramMetadata = (hasBias, cacheHint) => ({
name: 'GroupedConv',
inputNames: hasBias ? ['X', 'W', 'Bias'] : ['X', 'W'],
inputTypes: hasBias ? [types_1.TextureType.unpacked, types_1.TextureType.unpacked, types_1.TextureType.unpacked] :
[types_1.TextureType.unpacked, types_1.TextureType.unpacked],
cacheHint
});
const createUnpackedGroupedConvProgramInfo = (inferenceHandler, inputs, metadata, attributes) => {
const hasBias = inputs.length > 2;
const processBias = hasBias ? 'value += getBias(output_channel);' : '';
const xShape = inputs[0].dims.slice();
const wShape = inputs[1].dims.slice();
const outputChannelsPerGroup = wShape[0] / attributes.group;
instrument_1.Logger.verbose('GroupedConv', `autpPad:${attributes.autoPad}, dilations:${attributes.dilations}, group:${attributes.group}, kernelShape:${attributes.kernelShape}, pads:${attributes.pads}, strides:${attributes.strides}`);
const outputShape = (0, conv_1.calculateOutputShape)(xShape, wShape, attributes.dilations, attributes.pads, attributes.strides);
const glsl = (0, glsl_source_1.getGlsl)(inferenceHandler.session.backend.glContext.version);
const { activationFunction, applyActivation } = (0, fuse_utils_1.getActivationSnippet)(attributes);
const shaderSource = `
const ivec2 strides = ivec2(${attributes.strides[0]}, ${attributes.strides[1]});
const ivec2 pads = ivec2(${attributes.pads[0]}, ${attributes.pads[1]});
${activationFunction}
void main() {
ivec4 coords = getOutputCoords();
int batch = coords.x;
int output_channel = coords.y;
ivec2 xRCCorner = coords.zw * strides - pads;
int group_id = output_channel / ${outputChannelsPerGroup};
float value = 0.0;
for (int wInChannel = 0; wInChannel < ${wShape[1]}; wInChannel++) {
int input_channel = group_id * ${wShape[1]} + wInChannel;
for (int wHeight = 0; wHeight < ${wShape[2]}; wHeight++) {
int xHeight = xRCCorner.x + wHeight * ${attributes.dilations[0]};
if (xHeight < 0 || xHeight >= ${xShape[2]}) {
continue;
}
for (int wWidth = 0; wWidth < ${wShape[3]}; wWidth++) {
int xWidth = xRCCorner.y + wWidth * ${attributes.dilations[1]};
if (xWidth < 0 || xWidth >= ${xShape[3]}) {
continue;
}
float xVal = getX(batch, input_channel, xWidth, xHeight);
float wVal = getW(output_channel, wInChannel, wWidth, wHeight);
value += xVal*wVal;
}
}
}
${processBias}
${applyActivation}
${glsl.output} = vec4(value, .0, .0, .0);
}
`;
return Object.assign(Object.assign({}, metadata), { output: { dims: outputShape, type: inputs[0].type, textureType: types_1.TextureType.unpacked }, shaderSource, hasMain: true });
};
const createUnpackedGroupedConvProgramInfoLoader = (inferenceHandler, inputs, attributes) => {
const metadata = createUnpackedGroupedConvProgramMetadata(inputs.length > 2, attributes.cacheKey);
return Object.assign(Object.assign({}, metadata), { get: () => createUnpackedGroupedConvProgramInfo(inferenceHandler, inputs, metadata, attributes) });
};
exports.createUnpackedGroupedConvProgramInfoLoader = createUnpackedGroupedConvProgramInfoLoader;
//# sourceMappingURL=conv-grouped.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"conv-grouped.js","sourceRoot":"","sources":["conv-grouped.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,92 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import {Logger} from '../../../instrument';
import {Tensor} from '../../../tensor';
import {getGlsl} from '../glsl-source';
import {WebGLInferenceHandler} from '../inference-handler';
import {ProgramInfo, ProgramInfoLoader, ProgramMetadata, TextureType} from '../types';
import {calculateOutputShape, ConvAttributes} from './conv';
import {getActivationSnippet} from './fuse-utils';
const createUnpackedGroupedConvProgramMetadata = (hasBias: boolean, cacheHint: string): ProgramMetadata => ({
name: 'GroupedConv',
inputNames: hasBias ? ['X', 'W', 'Bias'] : ['X', 'W'],
inputTypes: hasBias ? [TextureType.unpacked, TextureType.unpacked, TextureType.unpacked] :
[TextureType.unpacked, TextureType.unpacked],
cacheHint
});
const createUnpackedGroupedConvProgramInfo =
(inferenceHandler: WebGLInferenceHandler, inputs: readonly Tensor[], metadata: ProgramMetadata,
attributes: ConvAttributes): ProgramInfo => {
const hasBias = inputs.length > 2;
const processBias = hasBias ? 'value += getBias(output_channel);' : '';
const xShape = inputs[0].dims.slice();
const wShape = inputs[1].dims.slice();
const outputChannelsPerGroup = wShape[0] / attributes.group;
Logger.verbose(
'GroupedConv',
`autpPad:${attributes.autoPad}, dilations:${attributes.dilations}, group:${attributes.group}, kernelShape:${
attributes.kernelShape}, pads:${attributes.pads}, strides:${attributes.strides}`);
const outputShape =
calculateOutputShape(xShape, wShape, attributes.dilations, attributes.pads, attributes.strides);
const glsl = getGlsl(inferenceHandler.session.backend.glContext.version);
const {activationFunction, applyActivation} = getActivationSnippet(attributes);
const shaderSource = `
const ivec2 strides = ivec2(${attributes.strides[0]}, ${attributes.strides[1]});
const ivec2 pads = ivec2(${attributes.pads[0]}, ${attributes.pads[1]});
${activationFunction}
void main() {
ivec4 coords = getOutputCoords();
int batch = coords.x;
int output_channel = coords.y;
ivec2 xRCCorner = coords.zw * strides - pads;
int group_id = output_channel / ${outputChannelsPerGroup};
float value = 0.0;
for (int wInChannel = 0; wInChannel < ${wShape[1]}; wInChannel++) {
int input_channel = group_id * ${wShape[1]} + wInChannel;
for (int wHeight = 0; wHeight < ${wShape[2]}; wHeight++) {
int xHeight = xRCCorner.x + wHeight * ${attributes.dilations[0]};
if (xHeight < 0 || xHeight >= ${xShape[2]}) {
continue;
}
for (int wWidth = 0; wWidth < ${wShape[3]}; wWidth++) {
int xWidth = xRCCorner.y + wWidth * ${attributes.dilations[1]};
if (xWidth < 0 || xWidth >= ${xShape[3]}) {
continue;
}
float xVal = getX(batch, input_channel, xWidth, xHeight);
float wVal = getW(output_channel, wInChannel, wWidth, wHeight);
value += xVal*wVal;
}
}
}
${processBias}
${applyActivation}
${glsl.output} = vec4(value, .0, .0, .0);
}
`;
return {
...metadata,
output: {dims: outputShape, type: inputs[0].type, textureType: TextureType.unpacked},
shaderSource,
hasMain: true,
};
};
export const createUnpackedGroupedConvProgramInfoLoader =
(inferenceHandler: WebGLInferenceHandler, inputs: readonly Tensor[], attributes: ConvAttributes):
ProgramInfoLoader => {
const metadata = createUnpackedGroupedConvProgramMetadata(inputs.length > 2, attributes.cacheKey);
return {
...metadata,
get: () => createUnpackedGroupedConvProgramInfo(inferenceHandler, inputs, metadata, attributes)
};
};

View File

@@ -0,0 +1,36 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.conv2DPacked = exports.conv2DPackedPointwise = void 0;
const conv_1 = require("./conv");
const im2col_pack_1 = require("./im2col-pack");
const matmul_pack_1 = require("./matmul-pack");
const conv2DPackedPointwise = (inferenceHandler, inputs, attributes) => {
const xshape = inputs[0].dims;
const kshape = inputs[1].dims;
const outputShape = (0, conv_1.calculateOutputShape)(xshape, kshape, attributes.dilations, attributes.pads, attributes.strides);
const reshapedX = inferenceHandler.reshapePacked(inputs[0], [xshape[1], xshape[2] * xshape[3]]);
const reshapedK = inferenceHandler.reshapePacked(inputs[1], [kshape[0], kshape[1]]);
const matmulInputs = inputs.length > 2 ? [reshapedK, reshapedX, inputs[2]] : [reshapedK, reshapedX];
const matmulOutput = inferenceHandler.run((0, matmul_pack_1.createPackedMatmulProgramInfoLoader)(inferenceHandler, matmulInputs, attributes), matmulInputs);
return inferenceHandler.reshapePacked(matmulOutput, outputShape);
};
exports.conv2DPackedPointwise = conv2DPackedPointwise;
const conv2DPacked = (inferenceHandler, inputs, attributes) => {
const xshape = inputs[0].dims;
const kshape = inputs[1].dims;
const outputShape = (0, conv_1.calculateOutputShape)(xshape, kshape, attributes.dilations, attributes.pads, attributes.strides);
// run im2col
const im2colOutput = inferenceHandler.run((0, im2col_pack_1.createPackedIm2ColProgramInfoLoader)(inferenceHandler, inputs[0], inputs[1], outputShape, attributes), [inputs[0]]);
// reshape kernel
const kernelReshaped = inferenceHandler.reshapePacked(inputs[1], [kshape[0], kshape[1] * kshape[2] * kshape[3]]);
// run matmul
const matmulInputs = (inputs.length === 3) ? [kernelReshaped, im2colOutput, inputs[2]] : [kernelReshaped, im2colOutput];
const matmulOutput = inferenceHandler.run((0, matmul_pack_1.createPackedMatmulProgramInfoLoader)(inferenceHandler, matmulInputs, attributes), matmulInputs);
// reshape output
const outputReshaped = inferenceHandler.reshapePacked(matmulOutput, outputShape);
return outputReshaped;
};
exports.conv2DPacked = conv2DPacked;
//# sourceMappingURL=conv-pack.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"conv-pack.js","sourceRoot":"","sources":["conv-pack.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,50 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import {Tensor} from '../../../tensor';
import {WebGLInferenceHandler} from '../inference-handler';
import {calculateOutputShape, ConvAttributes} from './conv';
import {createPackedIm2ColProgramInfoLoader} from './im2col-pack';
import {createPackedMatmulProgramInfoLoader} from './matmul-pack';
export const conv2DPackedPointwise =
(inferenceHandler: WebGLInferenceHandler, inputs: readonly Tensor[], attributes: ConvAttributes): Tensor => {
const xshape = inputs[0].dims;
const kshape = inputs[1].dims;
const outputShape =
calculateOutputShape(xshape, kshape, attributes.dilations, attributes.pads, attributes.strides);
const reshapedX = inferenceHandler.reshapePacked(inputs[0], [xshape[1], xshape[2] * xshape[3]]);
const reshapedK = inferenceHandler.reshapePacked(inputs[1], [kshape[0], kshape[1]]);
const matmulInputs = inputs.length > 2 ? [reshapedK, reshapedX, inputs[2]] : [reshapedK, reshapedX];
const matmulOutput = inferenceHandler.run(
createPackedMatmulProgramInfoLoader(inferenceHandler, matmulInputs, attributes), matmulInputs);
return inferenceHandler.reshapePacked(matmulOutput, outputShape);
};
export const conv2DPacked =
(inferenceHandler: WebGLInferenceHandler, inputs: readonly Tensor[], attributes: ConvAttributes): Tensor => {
const xshape = inputs[0].dims;
const kshape = inputs[1].dims;
const outputShape =
calculateOutputShape(xshape, kshape, attributes.dilations, attributes.pads, attributes.strides);
// run im2col
const im2colOutput = inferenceHandler.run(
createPackedIm2ColProgramInfoLoader(inferenceHandler, inputs[0], inputs[1], outputShape, attributes),
[inputs[0]]);
// reshape kernel
const kernelReshaped = inferenceHandler.reshapePacked(inputs[1], [kshape[0], kshape[1] * kshape[2] * kshape[3]]);
// run matmul
const matmulInputs =
(inputs.length === 3) ? [kernelReshaped, im2colOutput, inputs[2]] : [kernelReshaped, im2colOutput];
const matmulOutput = inferenceHandler.run(
createPackedMatmulProgramInfoLoader(inferenceHandler, matmulInputs, attributes), matmulInputs);
// reshape output
const outputReshaped = inferenceHandler.reshapePacked(matmulOutput, outputShape);
return outputReshaped;
};

View File

@@ -0,0 +1,198 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseConvTransposeAttributes = exports.convTranspose = void 0;
const attribute_with_cache_key_1 = require("../../../attribute-with-cache-key");
const glsl_source_1 = require("../glsl-source");
const types_1 = require("../types");
const fuse_utils_1 = require("./fuse-utils");
const computeTotalPad = (inDim, stride, adj, kernel, dilation, outSize) => (inDim - 1) * stride + adj + (kernel - 1) * dilation + 1 - outSize;
const distributePadding = (totalPad, autoPad, pads, head, tail) => {
const smallPad = Math.floor(totalPad / 2);
if (autoPad === 'SAME_UPPER') {
pads[head] = smallPad;
pads[tail] = totalPad - smallPad;
}
else if (autoPad === 'SAME_LOWER') {
pads[head] = totalPad - smallPad;
pads[tail] = smallPad;
}
};
const calculateOutputShapeAndPads = (inputShape, kernelShape, dilations, autoPad, pads, strides, outputPadding, outputShape) => {
const spatialRank = inputShape.length - 2;
const updateShape = outputShape.length === 0;
for (let i = 0; i < spatialRank; ++i) {
const outSize = updateShape ? inputShape[i + 2] * strides[i] : outputShape[i];
const totalPad = computeTotalPad(inputShape[i + 2], strides[i], pads[i], kernelShape[i], dilations[i], outSize);
distributePadding(totalPad, autoPad, pads, i, i + spatialRank);
if (updateShape) {
outputShape.push(strides[i] * (inputShape[i + 2] - 1) + outputPadding[i] + (kernelShape[i] - 1) * dilations[i] + 1 -
pads[i] - pads[i + spatialRank]);
}
}
};
const convTranspose = (inferenceHandler, inputs, attributes) => {
validateInputs(inputs, attributes); // currently will fail if not convTranspose2D
return convTranspose2d(inferenceHandler, inputs, attributes);
};
exports.convTranspose = convTranspose;
const convTranspose2d = (inferenceHandler, inputs, attributes) => {
const adjustedAttributes = getAdjustedConvTransposeAttributes(attributes, inputs);
return [convTranspose2DUnpacked(inferenceHandler, inputs, adjustedAttributes)];
};
const createConvTransposeProgramMetadata = (hasBias, cacheHint) => ({
name: 'ConvTranspose',
inputNames: hasBias ? ['X', 'W', 'B'] : ['X', 'W'],
inputTypes: hasBias ? [types_1.TextureType.unpacked, types_1.TextureType.unpacked, types_1.TextureType.unpacked] :
[types_1.TextureType.unpacked, types_1.TextureType.unpacked],
cacheHint
});
const createUnpackedConvTransposeProgramInfo = (inferenceHandler, inputs, metadata, attributes) => {
const hasBias = inputs.length > 2;
const valueInit = hasBias ? 'getB(output_channel)' : '0.0';
const xShape = inputs[0].dims;
const wShape = inputs[1].dims;
const outputChannelsPerGroup = wShape[1];
const inputChannelsPerGroup = wShape[0] / attributes.group;
const outputShape = [inputs[0].dims[0], inputs[1].dims[1] * attributes.group, ...attributes.outputShape];
const glsl = (0, glsl_source_1.getGlsl)(inferenceHandler.session.backend.glContext.version);
const { activationFunction, applyActivation } = (0, fuse_utils_1.getActivationSnippet)(attributes);
const shaderSource = `
const ivec2 strides = ivec2(${attributes.strides[0]}, ${attributes.strides[1]});
const ivec2 pads = ivec2(${attributes.pads[0]}, ${attributes.pads[1]});
${activationFunction}
void main() {
ivec4 coords = getOutputCoords();
int batch = coords.x;
int output_channel = coords.y;
ivec2 loc = coords.zw + pads;
int group_id = output_channel / ${outputChannelsPerGroup};
int wOutChannel = output_channel - group_id * ${outputChannelsPerGroup};
float value = ${valueInit};
for (int inChannelOffset = 0; inChannelOffset < ${inputChannelsPerGroup}; inChannelOffset++) {
int input_channel = group_id * ${inputChannelsPerGroup} + inChannelOffset;
for (int wWOff = 0; wWOff < ${wShape[2]}; wWOff++) {
for (int wHOff = 0; wHOff < ${wShape[3]}; wHOff++) {
ivec2 wOff = ivec2(wWOff * ${attributes.dilations[0]}, wHOff * ${attributes.dilations[1]});
ivec2 wLoc = loc - wOff;
ivec2 wLocIn = wLoc / strides;
if (
wLocIn * strides == wLoc &&
wLocIn.x >= 0 && wLocIn.x < ${xShape[2]} &&
wLocIn.y >= 0 && wLocIn.y < ${xShape[3]}
) {
float xVal = getX(batch, input_channel, wLocIn.y, wLocIn.x);
float wVal = getW(input_channel, wOutChannel, wHOff, wWOff);
value += xVal * wVal;
}
}
}
}
${applyActivation}
${glsl.output} = vec4(value, .0, .0, .0);
}
`;
return Object.assign(Object.assign({}, metadata), { output: { dims: outputShape, type: inputs[0].type, textureType: types_1.TextureType.unpacked }, shaderSource, hasMain: true });
};
const createUnpackedConvTransposeProgramInfoLoader = (inferenceHandler, inputs, attributes) => {
const metadata = createConvTransposeProgramMetadata(inputs.length > 2, attributes.cacheKey);
return Object.assign(Object.assign({}, metadata), { get: () => createUnpackedConvTransposeProgramInfo(inferenceHandler, inputs, metadata, attributes) });
};
const convTranspose2DUnpacked = (inferenceHandler, inputs, attributes) => {
const result = inferenceHandler.run(createUnpackedConvTransposeProgramInfoLoader(inferenceHandler, inputs, attributes), inputs);
return result;
};
const getAdjustedConvTransposeAttributes = (attributes, inputs) => {
const kernelShape = attributes.kernelShape.slice();
// if kernelShape is not specified in the attributes of this op, infer it from the weight tensor dims
if (attributes.kernelShape.length === 0) {
for (let i = 2; i < inputs[1].dims.length; ++i) {
kernelShape.push(inputs[1].dims[i]);
}
}
const pads = attributes.pads.slice();
const outputShape = attributes.outputShape.slice();
const inputShape = inputs[0].dims;
// If outputShape is not specified in the attributes of this op, infer it from the parameters
// Similarly, automatically infer pads if not specified
calculateOutputShapeAndPads(inputShape, kernelShape, attributes.dilations, attributes.autoPad, pads, attributes.strides, attributes.outputPadding, outputShape);
// always return a new object so does not modify the original attributes
const newAttributes = Object.assign({}, attributes);
Object.assign(newAttributes, { kernelShape, pads, outputShape, cacheKey: attributes.cacheKey });
return newAttributes;
};
const parseConvTransposeAttributes = (node) => {
const attributes = node.attributes;
const activationAttributes = (0, fuse_utils_1.parseInternalActivationAttributes)(attributes);
// TODO : Make this generic enough to compute default attributes for multi-dimensional conv
const autoPad = attributes.getString('auto_pad', 'NOTSET');
const dilations = attributes.getInts('dilations', [1, 1]);
const group = attributes.getInt('group', 1);
const kernelShape = attributes.getInts('kernel_shape', []);
const outputPadding = attributes.getInts('output_padding', [0, 0]);
const outputShape = attributes.getInts('output_shape', []);
const pads = attributes.getInts('pads', [0, 0, 0, 0]);
const strides = attributes.getInts('strides', [1, 1]);
return (0, attribute_with_cache_key_1.createAttributeWithCacheKey)(Object.assign({ autoPad, dilations, group, kernelShape, outputPadding, outputShape, pads, strides }, activationAttributes));
};
exports.parseConvTransposeAttributes = parseConvTransposeAttributes;
const validateInputs = (inputs, attributes) => {
// Refer to the below link for all input checks
// https://github.com/onnx/onnx/blob/main/docs/Operators.md#Conv
if (!inputs || (inputs.length !== 2 && inputs.length !== 3)) {
throw new Error('Conv requires 2 or 3 inputs');
}
// TODO : Need to add support for multi-dimensional conv
if (inputs[0].dims.length !== 4 || inputs[1].dims.length !== 4) {
throw new Error('currently only support 2-dimensional conv');
}
// FILTER_IN_CHANNEL should be equal to DATA_CHANNEL
const dataChannel = inputs[0].dims[1];
const filterInChannel = inputs[1].dims[0];
if (dataChannel !== filterInChannel) {
throw new Error('FILTER_IN_CHANNEL should be equal to DATA_CHANNEL');
}
const featureMaps = inputs[1].dims[1] * attributes.group;
// if bias is provided it should be 1D and the number of elements should be equal to the number of feature maps
if (inputs.length === 3 && (inputs[2].dims.length !== 1 || inputs[2].dims[0] !== featureMaps)) {
throw new Error('invalid bias');
}
const spatialRank = inputs[0].dims.length - 2;
// wrong dilations dimension
if (attributes.dilations.length !== spatialRank) {
throw new Error(`dilations should be ${spatialRank}D`);
}
// Wrong strides dimension
if (attributes.strides.length !== spatialRank) {
throw new Error(`strides should be ${spatialRank}D`);
}
// Wrong pads dimension
if (attributes.pads.length !== spatialRank * 2) {
throw new Error(`pads should be ${spatialRank * 2}D`);
}
// Wrong output padding dimension
if (attributes.outputPadding.length !== spatialRank) {
throw new Error(`output_padding should be ${spatialRank}D`);
}
// if kernelShape is specified, it's data length must be 2 less than dims length of the weights tensor
// (the first 2 dims are batch_size and channels)
if (attributes.kernelShape.length !== 0 && attributes.kernelShape.length !== inputs[1].dims.length - 2) {
throw new Error('invalid kernel shape');
}
// as with kernelShape, must have same number of spatial dims as input
if (attributes.outputShape.length !== 0 && attributes.outputShape.length !== inputs[0].dims.length - 2) {
throw new Error('invalid output shape');
}
// TODO : Need to add support for float64
if (inputs[0].type !== 'float32' || inputs[1].type !== 'float32') {
throw new Error('ConvTranspose input(X,W) should be float tensor');
}
if (inputs.length === 3 && inputs[2].type !== 'float32') {
throw new Error('ConvTranspose input(bias) should be float tensor');
}
};
//# sourceMappingURL=conv-transpose.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,259 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import {createAttributeWithCacheKey} from '../../../attribute-with-cache-key';
import {InferenceHandler} from '../../../backend';
import {Graph} from '../../../graph';
import {OperatorImplementation, OperatorInitialization} from '../../../operators';
import {Tensor} from '../../../tensor';
import {getGlsl} from '../glsl-source';
import {WebGLInferenceHandler} from '../inference-handler';
import {ProgramInfo, ProgramInfoLoader, ProgramMetadata, TextureType} from '../types';
import {ConvAttributes} from './conv';
import {getActivationSnippet, parseInternalActivationAttributes} from './fuse-utils';
const computeTotalPad =
(inDim: number, stride: number, adj: number, kernel: number, dilation: number, outSize: number) =>
(inDim - 1) * stride + adj + (kernel - 1) * dilation + 1 - outSize;
const distributePadding = (totalPad: number, autoPad: string, pads: number[], head: number, tail: number) => {
const smallPad = Math.floor(totalPad / 2);
if (autoPad === 'SAME_UPPER') {
pads[head] = smallPad;
pads[tail] = totalPad - smallPad;
} else if (autoPad === 'SAME_LOWER') {
pads[head] = totalPad - smallPad;
pads[tail] = smallPad;
}
};
const calculateOutputShapeAndPads =
(inputShape: readonly number[], kernelShape: readonly number[], dilations: readonly number[], autoPad: string,
pads: number[], strides: readonly number[], outputPadding: readonly number[], outputShape: number[]) => {
const spatialRank = inputShape.length - 2;
const updateShape = outputShape.length === 0;
for (let i = 0; i < spatialRank; ++i) {
const outSize = updateShape ? inputShape[i + 2] * strides[i] : outputShape[i];
const totalPad = computeTotalPad(inputShape[i + 2], strides[i], pads[i], kernelShape[i], dilations[i], outSize);
distributePadding(totalPad, autoPad, pads, i, i + spatialRank);
if (updateShape) {
outputShape.push(
strides[i] * (inputShape[i + 2] - 1) + outputPadding[i] + (kernelShape[i] - 1) * dilations[i] + 1 -
pads[i] - pads[i + spatialRank]);
}
}
};
export interface ConvTransposeAttributes extends ConvAttributes {
readonly outputPadding: readonly number[];
readonly outputShape: readonly number[];
}
export const convTranspose: OperatorImplementation<ConvTransposeAttributes> =
(inferenceHandler: InferenceHandler, inputs: Tensor[], attributes: ConvTransposeAttributes): Tensor[] => {
validateInputs(inputs, attributes); // currently will fail if not convTranspose2D
return convTranspose2d(inferenceHandler, inputs, attributes);
};
const convTranspose2d: OperatorImplementation<ConvTransposeAttributes> =
(inferenceHandler: WebGLInferenceHandler, inputs: Tensor[], attributes: ConvTransposeAttributes): Tensor[] => {
const adjustedAttributes = getAdjustedConvTransposeAttributes(attributes, inputs);
return [convTranspose2DUnpacked(inferenceHandler, inputs, adjustedAttributes)];
};
const createConvTransposeProgramMetadata = (hasBias: boolean, cacheHint: string) => ({
name: 'ConvTranspose',
inputNames: hasBias ? ['X', 'W', 'B'] : ['X', 'W'],
inputTypes: hasBias ? [TextureType.unpacked, TextureType.unpacked, TextureType.unpacked] :
[TextureType.unpacked, TextureType.unpacked],
cacheHint
});
const createUnpackedConvTransposeProgramInfo =
(inferenceHandler: WebGLInferenceHandler, inputs: readonly Tensor[], metadata: ProgramMetadata,
attributes: ConvTransposeAttributes): ProgramInfo => {
const hasBias = inputs.length > 2;
const valueInit = hasBias ? 'getB(output_channel)' : '0.0';
const xShape = inputs[0].dims;
const wShape = inputs[1].dims;
const outputChannelsPerGroup = wShape[1];
const inputChannelsPerGroup = wShape[0] / attributes.group;
const outputShape = [inputs[0].dims[0], inputs[1].dims[1] * attributes.group, ...attributes.outputShape];
const glsl = getGlsl(inferenceHandler.session.backend.glContext.version);
const {activationFunction, applyActivation} = getActivationSnippet(attributes);
const shaderSource = `
const ivec2 strides = ivec2(${attributes.strides[0]}, ${attributes.strides[1]});
const ivec2 pads = ivec2(${attributes.pads[0]}, ${attributes.pads[1]});
${activationFunction}
void main() {
ivec4 coords = getOutputCoords();
int batch = coords.x;
int output_channel = coords.y;
ivec2 loc = coords.zw + pads;
int group_id = output_channel / ${outputChannelsPerGroup};
int wOutChannel = output_channel - group_id * ${outputChannelsPerGroup};
float value = ${valueInit};
for (int inChannelOffset = 0; inChannelOffset < ${inputChannelsPerGroup}; inChannelOffset++) {
int input_channel = group_id * ${inputChannelsPerGroup} + inChannelOffset;
for (int wWOff = 0; wWOff < ${wShape[2]}; wWOff++) {
for (int wHOff = 0; wHOff < ${wShape[3]}; wHOff++) {
ivec2 wOff = ivec2(wWOff * ${attributes.dilations[0]}, wHOff * ${attributes.dilations[1]});
ivec2 wLoc = loc - wOff;
ivec2 wLocIn = wLoc / strides;
if (
wLocIn * strides == wLoc &&
wLocIn.x >= 0 && wLocIn.x < ${xShape[2]} &&
wLocIn.y >= 0 && wLocIn.y < ${xShape[3]}
) {
float xVal = getX(batch, input_channel, wLocIn.y, wLocIn.x);
float wVal = getW(input_channel, wOutChannel, wHOff, wWOff);
value += xVal * wVal;
}
}
}
}
${applyActivation}
${glsl.output} = vec4(value, .0, .0, .0);
}
`;
return {
...metadata,
output: {dims: outputShape, type: inputs[0].type, textureType: TextureType.unpacked},
shaderSource,
hasMain: true,
};
};
const createUnpackedConvTransposeProgramInfoLoader =
(inferenceHandler: WebGLInferenceHandler, inputs: readonly Tensor[], attributes: ConvTransposeAttributes):
ProgramInfoLoader => {
const metadata = createConvTransposeProgramMetadata(inputs.length > 2, attributes.cacheKey);
return {
...metadata,
get: () => createUnpackedConvTransposeProgramInfo(inferenceHandler, inputs, metadata, attributes)
};
};
const convTranspose2DUnpacked =
(inferenceHandler: WebGLInferenceHandler, inputs: readonly Tensor[], attributes: ConvTransposeAttributes):
Tensor => {
const result = inferenceHandler.run(
createUnpackedConvTransposeProgramInfoLoader(inferenceHandler, inputs, attributes), inputs);
return result;
};
const getAdjustedConvTransposeAttributes = <T extends ConvTransposeAttributes>(attributes: T, inputs: Tensor[]): T => {
const kernelShape = attributes.kernelShape.slice();
// if kernelShape is not specified in the attributes of this op, infer it from the weight tensor dims
if (attributes.kernelShape.length === 0) {
for (let i = 2; i < inputs[1].dims.length; ++i) {
kernelShape.push(inputs[1].dims[i]);
}
}
const pads = attributes.pads.slice();
const outputShape = attributes.outputShape.slice();
const inputShape = inputs[0].dims;
// If outputShape is not specified in the attributes of this op, infer it from the parameters
// Similarly, automatically infer pads if not specified
calculateOutputShapeAndPads(
inputShape, kernelShape, attributes.dilations, attributes.autoPad, pads, attributes.strides,
attributes.outputPadding, outputShape);
// always return a new object so does not modify the original attributes
const newAttributes: T = Object.assign({}, attributes);
Object.assign(newAttributes, {kernelShape, pads, outputShape, cacheKey: attributes.cacheKey});
return newAttributes;
};
export const parseConvTransposeAttributes: OperatorInitialization<ConvTransposeAttributes> =
(node: Graph.Node): ConvTransposeAttributes => {
const attributes = node.attributes;
const activationAttributes = parseInternalActivationAttributes(attributes);
// TODO : Make this generic enough to compute default attributes for multi-dimensional conv
const autoPad = attributes.getString('auto_pad', 'NOTSET');
const dilations = attributes.getInts('dilations', [1, 1]);
const group = attributes.getInt('group', 1);
const kernelShape = attributes.getInts('kernel_shape', []);
const outputPadding = attributes.getInts('output_padding', [0, 0]);
const outputShape = attributes.getInts('output_shape', []);
const pads = attributes.getInts('pads', [0, 0, 0, 0]);
const strides = attributes.getInts('strides', [1, 1]);
return createAttributeWithCacheKey(
{autoPad, dilations, group, kernelShape, outputPadding, outputShape, pads, strides, ...activationAttributes});
};
const validateInputs = (inputs: Tensor[], attributes: ConvTransposeAttributes): void => {
// Refer to the below link for all input checks
// https://github.com/onnx/onnx/blob/main/docs/Operators.md#Conv
if (!inputs || (inputs.length !== 2 && inputs.length !== 3)) {
throw new Error('Conv requires 2 or 3 inputs');
}
// TODO : Need to add support for multi-dimensional conv
if (inputs[0].dims.length !== 4 || inputs[1].dims.length !== 4) {
throw new Error('currently only support 2-dimensional conv');
}
// FILTER_IN_CHANNEL should be equal to DATA_CHANNEL
const dataChannel = inputs[0].dims[1];
const filterInChannel = inputs[1].dims[0];
if (dataChannel !== filterInChannel) {
throw new Error('FILTER_IN_CHANNEL should be equal to DATA_CHANNEL');
}
const featureMaps = inputs[1].dims[1] * attributes.group;
// if bias is provided it should be 1D and the number of elements should be equal to the number of feature maps
if (inputs.length === 3 && (inputs[2].dims.length !== 1 || inputs[2].dims[0] !== featureMaps)) {
throw new Error('invalid bias');
}
const spatialRank = inputs[0].dims.length - 2;
// wrong dilations dimension
if (attributes.dilations.length !== spatialRank) {
throw new Error(`dilations should be ${spatialRank}D`);
}
// Wrong strides dimension
if (attributes.strides.length !== spatialRank) {
throw new Error(`strides should be ${spatialRank}D`);
}
// Wrong pads dimension
if (attributes.pads.length !== spatialRank * 2) {
throw new Error(`pads should be ${spatialRank * 2}D`);
}
// Wrong output padding dimension
if (attributes.outputPadding.length !== spatialRank) {
throw new Error(`output_padding should be ${spatialRank}D`);
}
// if kernelShape is specified, it's data length must be 2 less than dims length of the weights tensor
// (the first 2 dims are batch_size and channels)
if (attributes.kernelShape.length !== 0 && attributes.kernelShape.length !== inputs[1].dims.length - 2) {
throw new Error('invalid kernel shape');
}
// as with kernelShape, must have same number of spatial dims as input
if (attributes.outputShape.length !== 0 && attributes.outputShape.length !== inputs[0].dims.length - 2) {
throw new Error('invalid output shape');
}
// TODO : Need to add support for float64
if (inputs[0].type !== 'float32' || inputs[1].type !== 'float32') {
throw new Error('ConvTranspose input(X,W) should be float tensor');
}
if (inputs.length === 3 && inputs[2].type !== 'float32') {
throw new Error('ConvTranspose input(bias) should be float tensor');
}
};

View File

@@ -0,0 +1,143 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseConvAttributes = exports.conv = exports.calculateOutputShape = void 0;
const attribute_with_cache_key_1 = require("../../../attribute-with-cache-key");
const util_1 = require("../../../util");
const conv_grouped_1 = require("./conv-grouped");
const conv_pack_1 = require("./conv-pack");
const dot_product_1 = require("./dot-product");
const fuse_utils_1 = require("./fuse-utils");
const im2col_1 = require("./im2col");
const matmul_1 = require("./matmul");
const calculateOutputShape = (inputShape, kernelShape, dilations, adjustPads, strides) => {
const batchSize = inputShape[0];
const inputSpatialShape = inputShape.slice(2);
const spatialRank = inputSpatialShape.length;
const outChannels = kernelShape[0];
const kernelSpatialShape = kernelShape.slice(2);
const dilatedKernelShape = kernelSpatialShape.map((v, i) => v + (v - 1) * (dilations[i] - 1));
const inputSpatialShapeWithPad = inputSpatialShape.map((v, i) => v + adjustPads[i] + adjustPads[i + spatialRank]);
const outputSpatialShape = inputSpatialShapeWithPad.map((v, i) => Math.floor((v - dilatedKernelShape[i] + strides[i]) / strides[i]));
const outputShape = [batchSize, outChannels].concat(...outputSpatialShape);
return outputShape;
};
exports.calculateOutputShape = calculateOutputShape;
const conv = (inferenceHandler, inputs, attributes) => {
validateInputs(inputs, attributes); // currently will fail if not conv2D
return conv2d(inferenceHandler, inputs, attributes);
};
exports.conv = conv;
const conv2d = (inferenceHandler, inputs, attributes) => {
const adjustedAttributes = getAdjustedConvAttributes(attributes, inputs);
const packMode = inferenceHandler.session.pack;
const isPointwise = adjustedAttributes.kernelShape[0] === 1 && adjustedAttributes.kernelShape[1] === 1;
if (adjustedAttributes.group > 1) {
const result = inferenceHandler.run((0, conv_grouped_1.createUnpackedGroupedConvProgramInfoLoader)(inferenceHandler, inputs, adjustedAttributes), inputs);
return [result];
}
else if (isPointwise && packMode) {
return [conv2DUnpackedPointwise(inferenceHandler, inputs, adjustedAttributes)];
}
else if (packMode && inputs[0].dims.length === 4 && inputs[0].dims[0] === 1 && !isPointwise) {
return [(0, conv_pack_1.conv2DPacked)(inferenceHandler, inputs, adjustedAttributes)];
}
else {
return [conv2DUnpacked(inferenceHandler, inputs, adjustedAttributes)];
}
};
const conv2DUnpackedPointwise = (inferenceHandler, inputs, attributes) => {
const xshape = inputs[0].dims;
const kshape = inputs[1].dims;
const outputShape = (0, exports.calculateOutputShape)(xshape, kshape, attributes.dilations, attributes.pads, attributes.strides);
const reshapedX = inferenceHandler.reshapeUnpacked(inputs[0], [xshape[1], xshape[2] * xshape[3]]);
const reshapedK = inferenceHandler.reshapeUnpacked(inputs[1], [kshape[0], kshape[1]]);
const matmulInputs = inputs.length > 2 ? [reshapedK, reshapedX, inputs[2]] : [reshapedK, reshapedX];
const matmulOutput = inferenceHandler.run((0, matmul_1.createMatmulProgramInfoLoader)(matmulInputs, attributes), matmulInputs);
return inferenceHandler.reshapeUnpacked(matmulOutput, outputShape);
};
const conv2DUnpacked = (inferenceHandler, inputs, attributes) => {
const xshape = inputs[0].dims;
const kshape = inputs[1].dims;
const outputShape = (0, exports.calculateOutputShape)(xshape, kshape, attributes.dilations, attributes.pads, attributes.strides);
const xIm2Col = inferenceHandler.run((0, im2col_1.createIm2ColProgramInfoLoader)(inferenceHandler, inputs[0], inputs[1], outputShape, attributes), [inputs[0]]);
const dotProductInputs = inputs.length === 3 ? [xIm2Col, inputs[1], inputs[2]] : [xIm2Col, inputs[1]];
const output = inferenceHandler.run((0, dot_product_1.createDotProductProgramInfoLoader)(inferenceHandler, inputs, outputShape, attributes), dotProductInputs);
return output;
};
const getAdjustedConvAttributes = (attributes, inputs) => {
const kernelShape = attributes.kernelShape.slice();
// if kernelShape is not specified in the attributes of this op, infer it from the weight tensor dims
if (attributes.kernelShape.length === 0) {
for (let i = 2; i < inputs[1].dims.length; ++i) {
kernelShape.push(inputs[1].dims[i]);
}
}
const pads = attributes.pads.slice();
util_1.PoolConvUtil.adjustPadsBasedOnAutoPad(inputs[0].dims, attributes.strides, attributes.dilations, kernelShape, pads, attributes.autoPad);
// always return a new object so does not modify the original attributes
const newAttributes = Object.assign({}, attributes);
Object.assign(newAttributes, { kernelShape, pads, cacheKey: attributes.cacheKey });
return newAttributes;
};
const parseConvAttributes = (node) => {
const attributes = node.attributes;
const activationAttributes = (0, fuse_utils_1.parseInternalActivationAttributes)(attributes);
// TODO : Make this generic enough to compute default attributes for multi-dimensional conv
const autoPad = attributes.getString('auto_pad', 'NOTSET');
const dilations = attributes.getInts('dilations', [1, 1]);
const group = attributes.getInt('group', 1);
const kernelShape = attributes.getInts('kernel_shape', []);
const pads = attributes.getInts('pads', [0, 0, 0, 0]);
const strides = attributes.getInts('strides', [1, 1]);
return (0, attribute_with_cache_key_1.createAttributeWithCacheKey)(Object.assign({ autoPad, dilations, group, kernelShape, pads, strides }, activationAttributes));
};
exports.parseConvAttributes = parseConvAttributes;
const validateInputs = (inputs, attributes) => {
// Refer to the below link for all input checks
// https://github.com/onnx/onnx/blob/main/docs/Operators.md#Conv
if (!inputs || (inputs.length !== 2 && inputs.length !== 3)) {
throw new Error('Conv requires 2 or 3 inputs');
}
// TODO : Need to add support for multi-dimensional conv
if (inputs[0].dims.length !== 4 || inputs[1].dims.length !== 4) {
throw new Error('currently only support 2-dimensional conv');
}
// FILTER_IN_CHANNEL should be equal to DATA_CHANNEL
const dataChannel = inputs[0].dims[1];
const filterInChannel = inputs[1].dims[1] * attributes.group;
if (dataChannel !== filterInChannel) {
throw new Error('FILTER_IN_CHANNEL should be equal to DATA_CHANNEL');
}
// if bias is provided it should be 1D and the number of elements should be equal to the number of feature maps
if (inputs.length === 3 && (inputs[2].dims.length !== 1 || inputs[1].dims[0] !== inputs[2].dims[0])) {
throw new Error('invalid bias');
}
const spatialRank = inputs[0].dims.length - 2;
// wrong dilations dimension
if (attributes.dilations.length !== spatialRank) {
throw new Error(`dilations should be ${spatialRank}D`);
}
// Wrong strides dimension
if (attributes.strides.length !== spatialRank) {
throw new Error(`strides should be ${spatialRank}D`);
}
// Wrong pads dimension
if (attributes.pads.length !== spatialRank * 2) {
throw new Error(`pads should be ${spatialRank * 2}D`);
}
// if kernelShape is specified, it's data length must be 2 less than dims length of the weights tensor
// (the first 2 dims are batch_size and channels)
if (attributes.kernelShape.length !== 0 && attributes.kernelShape.length !== inputs[1].dims.length - 2) {
throw new Error('invalid kernel shape');
}
// TODO : Need to add support for float64
if (inputs[0].type !== 'float32' || inputs[1].type !== 'float32') {
throw new Error('Conv input(X,W) should be float tensor');
}
if (inputs.length === 3 && inputs[2].type !== 'float32') {
throw new Error('Conv input(bias) should be float tensor');
}
};
//# sourceMappingURL=conv.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,184 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import {AttributeWithCacheKey, createAttributeWithCacheKey} from '../../../attribute-with-cache-key';
import {InferenceHandler} from '../../../backend';
import {Graph} from '../../../graph';
import {OperatorImplementation, OperatorInitialization} from '../../../operators';
import {Tensor} from '../../../tensor';
import {PoolConvUtil} from '../../../util';
import {WebGLInferenceHandler} from '../inference-handler';
import {createUnpackedGroupedConvProgramInfoLoader} from './conv-grouped';
import {conv2DPacked} from './conv-pack';
import {createDotProductProgramInfoLoader} from './dot-product';
import {InternalActivationAttributes, parseInternalActivationAttributes} from './fuse-utils';
import {createIm2ColProgramInfoLoader} from './im2col';
import {createMatmulProgramInfoLoader} from './matmul';
export const calculateOutputShape =
(inputShape: readonly number[], kernelShape: readonly number[], dilations: readonly number[],
adjustPads: readonly number[], strides: readonly number[]): number[] => {
const batchSize = inputShape[0];
const inputSpatialShape = inputShape.slice(2);
const spatialRank = inputSpatialShape.length;
const outChannels = kernelShape[0];
const kernelSpatialShape = kernelShape.slice(2);
const dilatedKernelShape = kernelSpatialShape.map((v, i) => v + (v - 1) * (dilations[i] - 1));
const inputSpatialShapeWithPad = inputSpatialShape.map((v, i) => v + adjustPads[i] + adjustPads[i + spatialRank]);
const outputSpatialShape =
inputSpatialShapeWithPad.map((v, i) => Math.floor((v - dilatedKernelShape[i] + strides[i]) / strides[i]));
const outputShape = [batchSize, outChannels].concat(...outputSpatialShape);
return outputShape;
};
export interface ConvAttributes extends InternalActivationAttributes, AttributeWithCacheKey {
readonly autoPad: string;
readonly dilations: readonly number[];
readonly group: number;
readonly kernelShape: readonly number[];
readonly pads: readonly number[];
readonly strides: readonly number[];
}
export const conv: OperatorImplementation<ConvAttributes> =
(inferenceHandler: InferenceHandler, inputs: Tensor[], attributes: ConvAttributes): Tensor[] => {
validateInputs(inputs, attributes); // currently will fail if not conv2D
return conv2d(inferenceHandler, inputs, attributes);
};
const conv2d: OperatorImplementation<ConvAttributes> =
(inferenceHandler: WebGLInferenceHandler, inputs: Tensor[], attributes: ConvAttributes): Tensor[] => {
const adjustedAttributes = getAdjustedConvAttributes(attributes, inputs);
const packMode = inferenceHandler.session.pack;
const isPointwise = adjustedAttributes.kernelShape[0] === 1 && adjustedAttributes.kernelShape[1] === 1;
if (adjustedAttributes.group > 1) {
const result = inferenceHandler.run(
createUnpackedGroupedConvProgramInfoLoader(inferenceHandler, inputs, adjustedAttributes), inputs);
return [result];
} else if (isPointwise && packMode) {
return [conv2DUnpackedPointwise(inferenceHandler, inputs, adjustedAttributes)];
} else if (packMode && inputs[0].dims.length === 4 && inputs[0].dims[0] === 1 && !isPointwise) {
return [conv2DPacked(inferenceHandler, inputs, adjustedAttributes)];
} else {
return [conv2DUnpacked(inferenceHandler, inputs, adjustedAttributes)];
}
};
const conv2DUnpackedPointwise =
(inferenceHandler: WebGLInferenceHandler, inputs: readonly Tensor[], attributes: ConvAttributes): Tensor => {
const xshape = inputs[0].dims;
const kshape = inputs[1].dims;
const outputShape =
calculateOutputShape(xshape, kshape, attributes.dilations, attributes.pads, attributes.strides);
const reshapedX = inferenceHandler.reshapeUnpacked(inputs[0], [xshape[1], xshape[2] * xshape[3]]);
const reshapedK = inferenceHandler.reshapeUnpacked(inputs[1], [kshape[0], kshape[1]]);
const matmulInputs = inputs.length > 2 ? [reshapedK, reshapedX, inputs[2]] : [reshapedK, reshapedX];
const matmulOutput = inferenceHandler.run(createMatmulProgramInfoLoader(matmulInputs, attributes), matmulInputs);
return inferenceHandler.reshapeUnpacked(matmulOutput, outputShape);
};
const conv2DUnpacked =
(inferenceHandler: WebGLInferenceHandler, inputs: readonly Tensor[], attributes: ConvAttributes): Tensor => {
const xshape = inputs[0].dims;
const kshape = inputs[1].dims;
const outputShape =
calculateOutputShape(xshape, kshape, attributes.dilations, attributes.pads, attributes.strides);
const xIm2Col = inferenceHandler.run(
createIm2ColProgramInfoLoader(inferenceHandler, inputs[0], inputs[1], outputShape, attributes), [inputs[0]]);
const dotProductInputs = inputs.length === 3 ? [xIm2Col, inputs[1], inputs[2]] : [xIm2Col, inputs[1]];
const output = inferenceHandler.run(
createDotProductProgramInfoLoader(inferenceHandler, inputs, outputShape, attributes), dotProductInputs);
return output;
};
const getAdjustedConvAttributes = <T extends ConvAttributes>(attributes: T, inputs: Tensor[]): T => {
const kernelShape = attributes.kernelShape.slice();
// if kernelShape is not specified in the attributes of this op, infer it from the weight tensor dims
if (attributes.kernelShape.length === 0) {
for (let i = 2; i < inputs[1].dims.length; ++i) {
kernelShape.push(inputs[1].dims[i]);
}
}
const pads = attributes.pads.slice();
PoolConvUtil.adjustPadsBasedOnAutoPad(
inputs[0].dims, attributes.strides, attributes.dilations, kernelShape, pads, attributes.autoPad);
// always return a new object so does not modify the original attributes
const newAttributes: T = Object.assign({}, attributes);
Object.assign(newAttributes, {kernelShape, pads, cacheKey: attributes.cacheKey});
return newAttributes;
};
export const parseConvAttributes: OperatorInitialization<ConvAttributes> = (node: Graph.Node): ConvAttributes => {
const attributes = node.attributes;
const activationAttributes = parseInternalActivationAttributes(attributes);
// TODO : Make this generic enough to compute default attributes for multi-dimensional conv
const autoPad = attributes.getString('auto_pad', 'NOTSET');
const dilations = attributes.getInts('dilations', [1, 1]);
const group = attributes.getInt('group', 1);
const kernelShape = attributes.getInts('kernel_shape', []);
const pads = attributes.getInts('pads', [0, 0, 0, 0]);
const strides = attributes.getInts('strides', [1, 1]);
return createAttributeWithCacheKey({autoPad, dilations, group, kernelShape, pads, strides, ...activationAttributes});
};
const validateInputs = (inputs: Tensor[], attributes: ConvAttributes): void => {
// Refer to the below link for all input checks
// https://github.com/onnx/onnx/blob/main/docs/Operators.md#Conv
if (!inputs || (inputs.length !== 2 && inputs.length !== 3)) {
throw new Error('Conv requires 2 or 3 inputs');
}
// TODO : Need to add support for multi-dimensional conv
if (inputs[0].dims.length !== 4 || inputs[1].dims.length !== 4) {
throw new Error('currently only support 2-dimensional conv');
}
// FILTER_IN_CHANNEL should be equal to DATA_CHANNEL
const dataChannel = inputs[0].dims[1];
const filterInChannel = inputs[1].dims[1] * attributes.group;
if (dataChannel !== filterInChannel) {
throw new Error('FILTER_IN_CHANNEL should be equal to DATA_CHANNEL');
}
// if bias is provided it should be 1D and the number of elements should be equal to the number of feature maps
if (inputs.length === 3 && (inputs[2].dims.length !== 1 || inputs[1].dims[0] !== inputs[2].dims[0])) {
throw new Error('invalid bias');
}
const spatialRank = inputs[0].dims.length - 2;
// wrong dilations dimension
if (attributes.dilations.length !== spatialRank) {
throw new Error(`dilations should be ${spatialRank}D`);
}
// Wrong strides dimension
if (attributes.strides.length !== spatialRank) {
throw new Error(`strides should be ${spatialRank}D`);
}
// Wrong pads dimension
if (attributes.pads.length !== spatialRank * 2) {
throw new Error(`pads should be ${spatialRank * 2}D`);
}
// if kernelShape is specified, it's data length must be 2 less than dims length of the weights tensor
// (the first 2 dims are batch_size and channels)
if (attributes.kernelShape.length !== 0 && attributes.kernelShape.length !== inputs[1].dims.length - 2) {
throw new Error('invalid kernel shape');
}
// TODO : Need to add support for float64
if (inputs[0].type !== 'float32' || inputs[1].type !== 'float32') {
throw new Error('Conv input(X,W) should be float tensor');
}
if (inputs.length === 3 && inputs[2].type !== 'float32') {
throw new Error('Conv input(bias) should be float tensor');
}
};

View File

@@ -0,0 +1,62 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseDepthToSpaceAttributes = exports.depthToSpace = void 0;
const transpose_1 = require("./transpose");
const depthToSpace = (inferenceHandler, inputs, attributes) => {
validateInputs(inputs);
const blocksize = attributes.blocksize;
const blocksizeSqr = blocksize * blocksize;
const transposePerm = attributes.mode === 'DCR' ? [0, 3, 4, 1, 5, 2] : [0, 1, 4, 2, 5, 3];
const firstReshapeShape = attributes.mode === 'DCR' ?
[
inputs[0].dims[0], blocksize, blocksize, inputs[0].dims[1] / blocksizeSqr, inputs[0].dims[2],
inputs[0].dims[3]
] :
[
inputs[0].dims[0], inputs[0].dims[1] / blocksizeSqr, blocksize, blocksize, inputs[0].dims[2],
inputs[0].dims[3]
];
// const transpose = new WebGLTranspose();
// const attributes = new Attribute(undefined);
// attributes.set('perm', 'ints', transposePerm);
// transpose.initialize(attributes);
// First reshape
const firstReshapedTensor = inferenceHandler.reshapeUnpacked(inputs[0], firstReshapeShape);
// transpose
const transposeAttributes = { perm: transposePerm, cacheKey: `${transposePerm}` };
const [transposeOutput] = (0, transpose_1.transpose)(inferenceHandler, [firstReshapedTensor], transposeAttributes);
// Second reshape
const secondReshapeShape = [
inputs[0].dims[0], inputs[0].dims[1] / blocksizeSqr, inputs[0].dims[2] * blocksize,
inputs[0].dims[3] * blocksize
];
const result = inferenceHandler.reshapeUnpacked(transposeOutput, secondReshapeShape);
return [result];
};
exports.depthToSpace = depthToSpace;
const parseDepthToSpaceAttributes = (node) => {
// processing node attributes
const blocksize = node.attributes.getInt('blocksize');
if (blocksize < 1) {
throw new Error(`blocksize must be >= 1, but got : ${blocksize} for DepthToSpace`);
}
const mode = node.attributes.getString('mode', 'DCR');
if (mode !== 'DCR' && mode !== 'CRD') {
throw new Error(`unrecognized mode: ${mode} for DepthToSpace`);
}
return { mode, blocksize };
};
exports.parseDepthToSpaceAttributes = parseDepthToSpaceAttributes;
const validateInputs = (inputs) => {
if (inputs.length !== 1) {
throw new Error(`DepthToSpace expect 1 inputs, but got ${inputs.length}`);
}
// Input has to be a 4-D tensor
// TODO: Support string depth-to-space.
if (inputs[0].type === 'string' || inputs[0].dims.length !== 4) {
throw new TypeError('DepthToSpace input should be a 4-D numeric tensor');
}
};
//# sourceMappingURL=depth-to-space.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"depth-to-space.js","sourceRoot":"","sources":["depth-to-space.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,77 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import {Graph} from '../../../graph';
import {OperatorImplementation, OperatorInitialization} from '../../../operators';
import {Tensor} from '../../../tensor';
import {WebGLInferenceHandler} from '../inference-handler';
import {transpose, TransposeAttributes} from './transpose';
export interface DepthToSpaceAttributes {
mode: 'DCR'|'CRD';
blocksize: number;
}
export const depthToSpace: OperatorImplementation<DepthToSpaceAttributes> =
(inferenceHandler: WebGLInferenceHandler, inputs: Tensor[], attributes: DepthToSpaceAttributes): Tensor[] => {
validateInputs(inputs);
const blocksize = attributes.blocksize;
const blocksizeSqr = blocksize * blocksize;
const transposePerm = attributes.mode === 'DCR' ? [0, 3, 4, 1, 5, 2] : [0, 1, 4, 2, 5, 3];
const firstReshapeShape = attributes.mode === 'DCR' ?
[
inputs[0].dims[0], blocksize, blocksize, inputs[0].dims[1] / blocksizeSqr, inputs[0].dims[2],
inputs[0].dims[3]
] :
[
inputs[0].dims[0], inputs[0].dims[1] / blocksizeSqr, blocksize, blocksize, inputs[0].dims[2],
inputs[0].dims[3]
];
// const transpose = new WebGLTranspose();
// const attributes = new Attribute(undefined);
// attributes.set('perm', 'ints', transposePerm);
// transpose.initialize(attributes);
// First reshape
const firstReshapedTensor = inferenceHandler.reshapeUnpacked(inputs[0], firstReshapeShape);
// transpose
const transposeAttributes: TransposeAttributes = {perm: transposePerm, cacheKey: `${transposePerm}`};
const [transposeOutput] = transpose(inferenceHandler, [firstReshapedTensor], transposeAttributes);
// Second reshape
const secondReshapeShape = [
inputs[0].dims[0], inputs[0].dims[1] / blocksizeSqr, inputs[0].dims[2] * blocksize,
inputs[0].dims[3] * blocksize
];
const result = inferenceHandler.reshapeUnpacked(transposeOutput, secondReshapeShape);
return [result];
};
export const parseDepthToSpaceAttributes: OperatorInitialization<DepthToSpaceAttributes> =
(node: Graph.Node): DepthToSpaceAttributes => {
// processing node attributes
const blocksize = node.attributes.getInt('blocksize');
if (blocksize < 1) {
throw new Error(`blocksize must be >= 1, but got : ${blocksize} for DepthToSpace`);
}
const mode = node.attributes.getString('mode', 'DCR');
if (mode !== 'DCR' && mode !== 'CRD') {
throw new Error(`unrecognized mode: ${mode} for DepthToSpace`);
}
return {mode, blocksize};
};
const validateInputs = (inputs: Tensor[]): void => {
if (inputs.length !== 1) {
throw new Error(`DepthToSpace expect 1 inputs, but got ${inputs.length}`);
}
// Input has to be a 4-D tensor
// TODO: Support string depth-to-space.
if (inputs[0].type === 'string' || inputs[0].dims.length !== 4) {
throw new TypeError('DepthToSpace input should be a 4-D numeric tensor');
}
};

View File

@@ -0,0 +1,60 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.createDotProductProgramInfoLoader = void 0;
const util_1 = require("../../../util");
const glsl_source_1 = require("../glsl-source");
const types_1 = require("../types");
const fuse_utils_1 = require("./fuse-utils");
const im2col_1 = require("./im2col");
const createDotProductProgramMetadata = (hasBias, attributes) => ({
name: 'ConvDotProduct',
inputNames: hasBias ? ['Im2Col', 'K', 'B'] : ['Im2Col', 'K'],
inputTypes: hasBias ? [types_1.TextureType.unpacked, types_1.TextureType.packedLastDimension, types_1.TextureType.unpacked] :
[types_1.TextureType.unpacked, types_1.TextureType.packedLastDimension],
cacheKey: attributes.activationCacheKey
});
const createDotProductProgramInfo = (inferenceHandler, metadata, inputs, outputShape, attributes) => {
const xshape = inputs[0].dims;
const kshape = inputs[1].dims;
const adjustedKernelShape = [kshape[0], Math.ceil((xshape[1] * kshape[2] * kshape[3]) / 4)];
const im2colShape = (0, im2col_1.calculateIm2ColDims)(xshape, kshape, outputShape);
const [kWidth, kHeight] = inferenceHandler.calculateTextureWidthAndHeight(adjustedKernelShape, types_1.TextureType.packedLastDimension);
const im2colStrides = util_1.ShapeUtil.computeStrides(im2colShape);
const [im2colWidth, im2colHeight] = inferenceHandler.calculateTextureWidthAndHeight(im2colShape, types_1.TextureType.packedLastDimension);
const rank = outputShape.length;
const initValue = (inputs.length < 3) ? '0.0' : '_B(b)';
const sharedDim = Math.ceil(xshape[1] * kshape[2] * kshape[3] / 4);
const { activationFunction, applyActivation } = (0, fuse_utils_1.getActivationSnippet)(attributes);
const glsl = (0, glsl_source_1.getGlsl)(inferenceHandler.session.backend.glContext.version);
const shaderSource = `
${activationFunction}
float process(int indices[${rank}]) {
int b[1];
b[0] = indices[1];
int im2col[4];
im2col[0] = indices[0];
im2col[1] = indices[2];
im2col[2] = indices[3];
int im2colOffset = im2col[0] * ${im2colStrides[0]} + im2col[1] * ${im2colStrides[1]} + im2col[2] * ${im2colStrides[2]};
int kernelOffset = indices[1] * ${adjustedKernelShape[1]};
float value = ${initValue};
for (int i = 0; i < ${sharedDim}; ++i) {
vec2 im2colCoords = offsetToCoords(im2colOffset, ${im2colWidth}, ${im2colHeight});
vec2 kernelCoords = offsetToCoords(kernelOffset, ${kWidth}, ${kHeight});
value += dot(${glsl.texture2D}(Im2Col, im2colCoords), ${glsl.texture2D}(K, kernelCoords));
++im2colOffset;
++kernelOffset;
}
${applyActivation}
return value;
}`;
return Object.assign(Object.assign({}, metadata), { output: { dims: outputShape, type: inputs[0].type, textureType: types_1.TextureType.unpacked }, shaderSource });
};
const createDotProductProgramInfoLoader = (inferenceHandler, inputs, outputShape, attributes) => {
const metadata = createDotProductProgramMetadata(inputs.length > 2, attributes);
return Object.assign(Object.assign({}, metadata), { get: () => createDotProductProgramInfo(inferenceHandler, metadata, inputs, outputShape, attributes) });
};
exports.createDotProductProgramInfoLoader = createDotProductProgramInfoLoader;
//# sourceMappingURL=dot-product.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"dot-product.js","sourceRoot":"","sources":["dot-product.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,78 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import {Tensor} from '../../../tensor';
import {ShapeUtil} from '../../../util';
import {getGlsl} from '../glsl-source';
import {WebGLInferenceHandler} from '../inference-handler';
import {ProgramInfo, ProgramInfoLoader, ProgramMetadata, TextureType} from '../types';
import {getActivationSnippet, InternalActivationAttributes} from './fuse-utils';
import {calculateIm2ColDims} from './im2col';
const createDotProductProgramMetadata = (hasBias: boolean, attributes: InternalActivationAttributes) => ({
name: 'ConvDotProduct',
inputNames: hasBias ? ['Im2Col', 'K', 'B'] : ['Im2Col', 'K'],
inputTypes: hasBias ? [TextureType.unpacked, TextureType.packedLastDimension, TextureType.unpacked] :
[TextureType.unpacked, TextureType.packedLastDimension],
cacheKey: attributes.activationCacheKey
});
const createDotProductProgramInfo =
(inferenceHandler: WebGLInferenceHandler, metadata: ProgramMetadata, inputs: readonly Tensor[],
outputShape: number[], attributes: InternalActivationAttributes): ProgramInfo => {
const xshape = inputs[0].dims;
const kshape = inputs[1].dims;
const adjustedKernelShape = [kshape[0], Math.ceil((xshape[1] * kshape[2] * kshape[3]) / 4)];
const im2colShape = calculateIm2ColDims(xshape, kshape, outputShape);
const [kWidth, kHeight] =
inferenceHandler.calculateTextureWidthAndHeight(adjustedKernelShape, TextureType.packedLastDimension);
const im2colStrides = ShapeUtil.computeStrides(im2colShape);
const [im2colWidth, im2colHeight] =
inferenceHandler.calculateTextureWidthAndHeight(im2colShape, TextureType.packedLastDimension);
const rank = outputShape.length;
const initValue = (inputs.length < 3) ? '0.0' : '_B(b)';
const sharedDim = Math.ceil(xshape[1] * kshape[2] * kshape[3] / 4);
const {activationFunction, applyActivation} = getActivationSnippet(attributes);
const glsl = getGlsl(inferenceHandler.session.backend.glContext.version);
const shaderSource = `
${activationFunction}
float process(int indices[${rank}]) {
int b[1];
b[0] = indices[1];
int im2col[4];
im2col[0] = indices[0];
im2col[1] = indices[2];
im2col[2] = indices[3];
int im2colOffset = im2col[0] * ${im2colStrides[0]} + im2col[1] * ${im2colStrides[1]} + im2col[2] * ${
im2colStrides[2]};
int kernelOffset = indices[1] * ${adjustedKernelShape[1]};
float value = ${initValue};
for (int i = 0; i < ${sharedDim}; ++i) {
vec2 im2colCoords = offsetToCoords(im2colOffset, ${im2colWidth}, ${im2colHeight});
vec2 kernelCoords = offsetToCoords(kernelOffset, ${kWidth}, ${kHeight});
value += dot(${glsl.texture2D}(Im2Col, im2colCoords), ${glsl.texture2D}(K, kernelCoords));
++im2colOffset;
++kernelOffset;
}
${applyActivation}
return value;
}`;
return {
...metadata,
output: {dims: outputShape, type: inputs[0].type, textureType: TextureType.unpacked},
shaderSource
};
};
export const createDotProductProgramInfoLoader =
(inferenceHandler: WebGLInferenceHandler, inputs: readonly Tensor[], outputShape: number[],
attributes: InternalActivationAttributes): ProgramInfoLoader => {
const metadata = createDotProductProgramMetadata(inputs.length > 2, attributes);
return {
...metadata,
get: () => createDotProductProgramInfo(inferenceHandler, metadata, inputs, outputShape, attributes)
};
};

View File

@@ -0,0 +1,31 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseFlattenAttributes = exports.flatten = void 0;
const util_1 = require("../../../util");
const flatten = (inferenceHandler, inputs, axis) => {
validateInputs(inputs, axis);
const outputDims = util_1.ShapeUtil.flattenShape(inputs[0].dims, axis);
return [inferenceHandler.reshapeUnpacked(inputs[0], outputDims)];
};
exports.flatten = flatten;
const parseFlattenAttributes = (node) => node.attributes.getInt('axis', 1); // default axis is 1
exports.parseFlattenAttributes = parseFlattenAttributes;
const validateInputs = (inputs, axis) => {
if (!inputs || inputs.length !== 1) {
throw new Error('Flatten requires 1 input.');
}
const r = inputs[0].dims.length;
if (r === 0) {
throw new Error('scalar tensor is not supported.');
}
if (axis < -r || axis > r) {
throw new Error('Invalid axis');
}
// TODO: Support string type
if (inputs[0].type === 'string') {
throw new Error('string tensor is not supported.');
}
};
//# sourceMappingURL=flatten.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"flatten.js","sourceRoot":"","sources":["flatten.ts"],"names":[],"mappings":";AAAA,4DAA4D;AAC5D,kCAAkC;;;AAKlC,wCAAwC;AAGjC,MAAM,OAAO,GAChB,CAAC,gBAAuC,EAAE,MAAgB,EAAE,IAAY,EAAY,EAAE;IACpF,cAAc,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAE7B,MAAM,UAAU,GAAG,gBAAS,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAChE,OAAO,CAAC,gBAAgB,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC;AACnE,CAAC,CAAC;AANO,QAAA,OAAO,WAMd;AAEC,MAAM,sBAAsB,GAAmC,CAAC,IAAgB,EAAU,EAAE,CAC/F,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAE,oBAAoB;AAD/C,QAAA,sBAAsB,0BACG;AAEtC,MAAM,cAAc,GAAG,CAAC,MAAgB,EAAE,IAAY,EAAQ,EAAE;IAC9D,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE;QAClC,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;KAC9C;IAED,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC;IAChC,IAAI,CAAC,KAAK,CAAC,EAAE;QACX,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;KACpD;IAED,IAAI,IAAI,GAAG,CAAC,CAAC,IAAI,IAAI,GAAG,CAAC,EAAE;QACzB,MAAM,IAAI,KAAK,CAAC,cAAc,CAAC,CAAC;KACjC;IAED,4BAA4B;IAC5B,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,EAAE;QAC/B,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;KACpD;AACH,CAAC,CAAC"}

View File

@@ -0,0 +1,39 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import {Graph} from '../../../graph';
import {OperatorImplementation, OperatorInitialization} from '../../../operators';
import {Tensor} from '../../../tensor';
import {ShapeUtil} from '../../../util';
import {WebGLInferenceHandler} from '../inference-handler';
export const flatten: OperatorImplementation<number> =
(inferenceHandler: WebGLInferenceHandler, inputs: Tensor[], axis: number): Tensor[] => {
validateInputs(inputs, axis);
const outputDims = ShapeUtil.flattenShape(inputs[0].dims, axis);
return [inferenceHandler.reshapeUnpacked(inputs[0], outputDims)];
};
export const parseFlattenAttributes: OperatorInitialization<number> = (node: Graph.Node): number =>
node.attributes.getInt('axis', 1); // default axis is 1
const validateInputs = (inputs: Tensor[], axis: number): void => {
if (!inputs || inputs.length !== 1) {
throw new Error('Flatten requires 1 input.');
}
const r = inputs[0].dims.length;
if (r === 0) {
throw new Error('scalar tensor is not supported.');
}
if (axis < -r || axis > r) {
throw new Error('Invalid axis');
}
// TODO: Support string type
if (inputs[0].type === 'string') {
throw new Error('string tensor is not supported.');
}
};

View File

@@ -0,0 +1,39 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseInternalActivationAttributes = exports.getActivationSnippet = void 0;
const util_1 = require("../../../util");
const unary_op_1 = require("./unary-op");
function getActivationSnippet(attributes) {
let func;
switch (attributes.activation) {
case 'Relu':
func = (0, unary_op_1.glslRelu)();
break;
case 'Sigmoid':
func = (0, unary_op_1.glslSigmoid)();
break;
case 'Clip':
func = (0, unary_op_1.glslClip)(attributes.clipMin, attributes.clipMax);
break;
// TODO: adding other activations that can be fused.
default:
return { activationFunction: '', applyActivation: '' };
}
const activationName = func.name;
const activationFunction = func.body;
const applyActivation = `value = ${activationName}_(value);`;
return { activationFunction, applyActivation };
}
exports.getActivationSnippet = getActivationSnippet;
const parseInternalActivationAttributes = (attributes) => {
const activation = attributes.getString('activation', '');
if (activation === 'Clip') {
const [clipMin, clipMax] = attributes.getFloats('activation_params', [util_1.MIN_CLIP, util_1.MAX_CLIP]);
return { activation, clipMax, clipMin, activationCacheKey: `${activation}:${clipMin},${clipMax}` };
}
return { activation, activationCacheKey: activation };
};
exports.parseInternalActivationAttributes = parseInternalActivationAttributes;
//# sourceMappingURL=fuse-utils.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"fuse-utils.js","sourceRoot":"","sources":["fuse-utils.ts"],"names":[],"mappings":";AAAA,4DAA4D;AAC5D,kCAAkC;;;AAGlC,wCAAiD;AAGjD,yCAA2D;AAS3D,SAAgB,oBAAoB,CAAC,UAAwC;IAC3E,IAAI,IAAuB,CAAC;IAC5B,QAAQ,UAAU,CAAC,UAAU,EAAE;QAC7B,KAAK,MAAM;YACT,IAAI,GAAG,IAAA,mBAAQ,GAAE,CAAC;YAClB,MAAM;QACR,KAAK,SAAS;YACZ,IAAI,GAAG,IAAA,sBAAW,GAAE,CAAC;YACrB,MAAM;QACR,KAAK,MAAM;YACT,IAAI,GAAG,IAAA,mBAAQ,EAAC,UAAU,CAAC,OAAQ,EAAE,UAAU,CAAC,OAAQ,CAAC,CAAC;YAC1D,MAAM;QACR,oDAAoD;QACpD;YACE,OAAO,EAAC,kBAAkB,EAAE,EAAE,EAAE,eAAe,EAAE,EAAE,EAAC,CAAC;KACxD;IAED,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC;IACjC,MAAM,kBAAkB,GAAG,IAAI,CAAC,IAAI,CAAC;IACrC,MAAM,eAAe,GAAG,WAAW,cAAc,WAAW,CAAC;IAC7D,OAAO,EAAC,kBAAkB,EAAE,eAAe,EAAC,CAAC;AAC/C,CAAC;AArBD,oDAqBC;AAEM,MAAM,iCAAiC,GAAG,CAAC,UAAqB,EAAgC,EAAE;IACvG,MAAM,UAAU,GAAG,UAAU,CAAC,SAAS,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;IAE1D,IAAI,UAAU,KAAK,MAAM,EAAE;QACzB,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG,UAAU,CAAC,SAAS,CAAC,mBAAmB,EAAE,CAAC,eAAQ,EAAE,eAAQ,CAAC,CAAC,CAAC;QAC3F,OAAO,EAAC,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,kBAAkB,EAAE,GAAG,UAAU,IAAI,OAAO,IAAI,OAAO,EAAE,EAAC,CAAC;KAClG;IACD,OAAO,EAAC,UAAU,EAAE,kBAAkB,EAAE,UAAU,EAAC,CAAC;AACtD,CAAC,CAAC;AARW,QAAA,iCAAiC,qCAQ5C"}

View File

@@ -0,0 +1,48 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import {Attribute} from '../../../attribute';
import {MAX_CLIP, MIN_CLIP} from '../../../util';
import {GlslValueFunction} from '../glsl-definitions';
import {glslClip, glslRelu, glslSigmoid} from './unary-op';
export interface InternalActivationAttributes {
readonly activation: string;
readonly clipMin?: number;
readonly clipMax?: number;
readonly activationCacheKey: string;
}
export function getActivationSnippet(attributes: InternalActivationAttributes) {
let func: GlslValueFunction;
switch (attributes.activation) {
case 'Relu':
func = glslRelu();
break;
case 'Sigmoid':
func = glslSigmoid();
break;
case 'Clip':
func = glslClip(attributes.clipMin!, attributes.clipMax!);
break;
// TODO: adding other activations that can be fused.
default:
return {activationFunction: '', applyActivation: ''};
}
const activationName = func.name;
const activationFunction = func.body;
const applyActivation = `value = ${activationName}_(value);`;
return {activationFunction, applyActivation};
}
export const parseInternalActivationAttributes = (attributes: Attribute): InternalActivationAttributes => {
const activation = attributes.getString('activation', '');
if (activation === 'Clip') {
const [clipMin, clipMax] = attributes.getFloats('activation_params', [MIN_CLIP, MAX_CLIP]);
return {activation, clipMax, clipMin, activationCacheKey: `${activation}:${clipMin},${clipMax}`};
}
return {activation, activationCacheKey: activation};
};

View File

@@ -0,0 +1,87 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseGatherAttributes = exports.gather = void 0;
const attribute_with_cache_key_1 = require("../../../attribute-with-cache-key");
const operators_1 = require("../../../operators");
const util_1 = require("../../../util");
const types_1 = require("../types");
const gather = (inferenceHandler, inputs, attributes) => {
validateInputs(inputs, attributes.axis);
const output = inferenceHandler.run(createGatherProgramInfoLoader(inferenceHandler, inputs, attributes), inputs);
return [output];
};
exports.gather = gather;
const parseGatherAttributes = (node) => (0, attribute_with_cache_key_1.createAttributeWithCacheKey)({ axis: node.attributes.getInt('axis', 0) });
exports.parseGatherAttributes = parseGatherAttributes;
const gatherProgramMetadata = {
name: 'Gather',
inputNames: ['A', 'B'],
inputTypes: [types_1.TextureType.unpacked, types_1.TextureType.unpacked],
};
const createGatherProgramInfo = (handler, metadata, inputs, axis) => {
const inputShape = inputs[0].dims.slice();
const indexDataShape = inputs[1].dims.slice();
const outputShape = new Array(inputShape.length + indexDataShape.length - 1);
axis = util_1.ShapeUtil.normalizeAxis(axis, inputShape.length);
const indexCopyOps = [];
for (let i = 0; i < outputShape.length; i++) {
// outputShape is divided into three parts: A, B, C
// |0 axis| axis + indexDataShape.length | end|
// | A | B | C |
//
// inputIdx: [A, inputs[1][B], C]
if (i < axis) { // A
outputShape[i] = inputShape[i];
indexCopyOps.push(`inputIdx[${i}] = outputIdx[${i}];`);
}
else {
if (i < axis + indexDataShape.length) { // B
outputShape[i] = indexDataShape[i - axis];
indexCopyOps.push(`indexDataIdx[${i - axis}] = outputIdx[${i}];`);
}
else { // C
outputShape[i] = inputShape[i - indexDataShape.length + 1]; // skip 1 for axis
indexCopyOps.push(`inputIdx[${i - indexDataShape.length + 1}] = outputIdx[${i}];`);
}
}
}
const orank = outputShape.length || 1;
const irank = inputShape.length;
const iDrank = indexDataShape.length || 1;
const shaderSource = `
float process(int outputIdx[${orank}]) {
int inputIdx[${irank}];
int indexDataIdx[${iDrank}];
indexDataIdx[0] = 0;
${indexCopyOps.join('\n ')}
int idx = int(_B(indexDataIdx));
inputIdx[${axis}] = idx < 0 ? idx + ${inputShape[axis]} : idx;
return _A(inputIdx);
}`;
return Object.assign(Object.assign({}, metadata), { output: { dims: outputShape, type: inputs[0].type, textureType: types_1.TextureType.unpacked }, shaderSource });
};
const createGatherProgramInfoLoader = (handler, inputs, attributes) => {
const metadata = Object.assign(Object.assign({}, gatherProgramMetadata), { cacheHint: attributes.cacheKey });
return Object.assign(Object.assign({}, metadata), { get: () => createGatherProgramInfo(handler, metadata, inputs, attributes.axis) });
};
const validateInputs = (inputs, axis) => {
if (!inputs || inputs.length !== 2) {
throw new Error('Gather requires 2 inputs.');
}
const tensorRank = inputs[0].dims.length;
if (tensorRank < 1) {
throw new Error('Invalid input shape.');
}
if (axis < -tensorRank || axis > tensorRank - 1) {
throw new Error('Invalid axis.');
}
if (operators_1.NUMBER_TYPES.indexOf(inputs[0].type) === -1) {
throw new Error('Invaid input type.');
}
if (inputs[1].type !== 'int32' && inputs[1].type !== 'int16') {
throw new Error('Invaid input type.');
}
};
//# sourceMappingURL=gather.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"gather.js","sourceRoot":"","sources":["gather.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,103 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import {AttributeWithCacheKey, createAttributeWithCacheKey} from '../../../attribute-with-cache-key';
import {Graph} from '../../../graph';
import {NUMBER_TYPES, OperatorImplementation, OperatorInitialization} from '../../../operators';
import {Tensor} from '../../../tensor';
import {ShapeUtil} from '../../../util';
import {WebGLInferenceHandler} from '../inference-handler';
import {ProgramInfo, ProgramInfoLoader, ProgramMetadata, TextureType} from '../types';
interface GatherAttributes extends AttributeWithCacheKey {
readonly axis: number;
}
export const gather: OperatorImplementation<GatherAttributes> =
(inferenceHandler: WebGLInferenceHandler, inputs: Tensor[], attributes: GatherAttributes): Tensor[] => {
validateInputs(inputs, attributes.axis);
const output = inferenceHandler.run(createGatherProgramInfoLoader(inferenceHandler, inputs, attributes), inputs);
return [output];
};
export const parseGatherAttributes: OperatorInitialization<GatherAttributes> = (node: Graph.Node): GatherAttributes =>
createAttributeWithCacheKey({axis: node.attributes.getInt('axis', 0)});
const gatherProgramMetadata = {
name: 'Gather',
inputNames: ['A', 'B'],
inputTypes: [TextureType.unpacked, TextureType.unpacked],
};
const createGatherProgramInfo =
(handler: WebGLInferenceHandler, metadata: ProgramMetadata, inputs: Tensor[], axis: number): ProgramInfo => {
const inputShape = inputs[0].dims.slice();
const indexDataShape = inputs[1].dims.slice();
const outputShape = new Array(inputShape.length + indexDataShape.length - 1);
axis = ShapeUtil.normalizeAxis(axis, inputShape.length);
const indexCopyOps: string[] = [];
for (let i = 0; i < outputShape.length; i++) {
// outputShape is divided into three parts: A, B, C
// |0 axis| axis + indexDataShape.length | end|
// | A | B | C |
//
// inputIdx: [A, inputs[1][B], C]
if (i < axis) { // A
outputShape[i] = inputShape[i];
indexCopyOps.push(`inputIdx[${i}] = outputIdx[${i}];`);
} else {
if (i < axis + indexDataShape.length) { // B
outputShape[i] = indexDataShape[i - axis];
indexCopyOps.push(`indexDataIdx[${i - axis}] = outputIdx[${i}];`);
} else { // C
outputShape[i] = inputShape[i - indexDataShape.length + 1]; // skip 1 for axis
indexCopyOps.push(`inputIdx[${i - indexDataShape.length + 1}] = outputIdx[${i}];`);
}
}
}
const orank = outputShape.length || 1;
const irank = inputShape.length;
const iDrank = indexDataShape.length || 1;
const shaderSource = `
float process(int outputIdx[${orank}]) {
int inputIdx[${irank}];
int indexDataIdx[${iDrank}];
indexDataIdx[0] = 0;
${indexCopyOps.join('\n ')}
int idx = int(_B(indexDataIdx));
inputIdx[${axis}] = idx < 0 ? idx + ${inputShape[axis]} : idx;
return _A(inputIdx);
}`;
return {
...metadata,
output: {dims: outputShape, type: inputs[0].type, textureType: TextureType.unpacked},
shaderSource
};
};
const createGatherProgramInfoLoader =
(handler: WebGLInferenceHandler, inputs: Tensor[], attributes: GatherAttributes): ProgramInfoLoader => {
const metadata = {...gatherProgramMetadata, cacheHint: attributes.cacheKey};
return {...metadata, get: () => createGatherProgramInfo(handler, metadata, inputs, attributes.axis)};
};
const validateInputs = (inputs: Tensor[], axis: number): void => {
if (!inputs || inputs.length !== 2) {
throw new Error('Gather requires 2 inputs.');
}
const tensorRank = inputs[0].dims.length;
if (tensorRank < 1) {
throw new Error('Invalid input shape.');
}
if (axis < -tensorRank || axis > tensorRank - 1) {
throw new Error('Invalid axis.');
}
if (NUMBER_TYPES.indexOf(inputs[0].type) === -1) {
throw new Error('Invaid input type.');
}
if (inputs[1].type !== 'int32' && inputs[1].type !== 'int16') {
throw new Error('Invaid input type.');
}
};

View File

@@ -0,0 +1,113 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseGemmAttributesV11 = exports.parseGemmAttributesV7 = exports.gemm = void 0;
const attribute_with_cache_key_1 = require("../../../attribute-with-cache-key");
const util_1 = require("../../../util");
const types_1 = require("../types");
const gemm = (inferenceHandler, inputs, attributes) => {
validateInputs(inputs, attributes);
const output = inferenceHandler.run(createGemmProgramInfoLoader(inputs, attributes), inputs);
return [output];
};
exports.gemm = gemm;
const parseGemmAttributes = (node, isOptionalC) => {
const transA = node.attributes.getInt('transA', 0) !== 0;
const transB = node.attributes.getInt('transB', 0) !== 0;
const alpha = node.attributes.getFloat('alpha', 1.0);
const beta = node.attributes.getFloat('beta', 1.0);
return (0, attribute_with_cache_key_1.createAttributeWithCacheKey)({ transA, transB, alpha, beta, isOptionalC });
};
const parseGemmAttributesV7 = (node) => parseGemmAttributes(node, false);
exports.parseGemmAttributesV7 = parseGemmAttributesV7;
const parseGemmAttributesV11 = (node) => parseGemmAttributes(node, true);
exports.parseGemmAttributesV11 = parseGemmAttributesV11;
const createGemmProgramInfoLoader = (inputs, attributes) => {
const metadata = {
name: 'Gemm',
inputNames: inputs.length === 3 ? ['A', 'B', 'C'] : ['A', 'B'],
inputTypes: inputs.length === 3 ? [types_1.TextureType.unpacked, types_1.TextureType.unpacked, types_1.TextureType.unpacked] :
[types_1.TextureType.unpacked, types_1.TextureType.unpacked],
key: attributes.cacheKey
};
return Object.assign(Object.assign({}, metadata), { get: () => createGemmProgramInfo(metadata, inputs, attributes) });
};
const createGemmProgramInfo = (metadata, inputs, attributes) => {
const aShape = inputs[0].dims.slice();
const bShape = inputs[1].dims.slice();
const [M, N] = util_1.GemmUtil.getShapeOfGemmResult(aShape, attributes.transA, bShape, attributes.transB, inputs.length === 3 ? inputs[2].dims : undefined);
const outputShape = [M, N];
if (!outputShape) {
throw new Error('Can\'t use gemm on the given tensors');
}
let sharedDim = aShape[aShape.length - 1];
let line = '';
if (attributes.transA) {
sharedDim = aShape[0];
}
if (attributes.transA && attributes.transB) {
line = 'value += _A_T(a) * _B_T(b);';
}
else if (attributes.transA && !attributes.transB) {
line = 'value += _A_T(a) * _B(b);';
}
else if (!attributes.transA && attributes.transB) {
line = 'value += _A(a) * _B_T(b);';
}
else if (!attributes.transA && !attributes.transB) {
line = 'value += _A(a) * _B(b);';
}
const rank = outputShape.length;
const declareC = inputs.length === 3 ? `int c[${inputs[2].dims.length}];` : '';
const broadcastC = inputs.length === 3 ? 'bcastIndices_C(indices, c);' : '';
const calculateC = inputs.length === 3 ? 'value += beta * _C(c);' : '';
const shaderSource = `
float process(int indices[${rank}]) {
int a[${rank}];
int b[${rank}];
${declareC}
copyVec(indices, a);
copyVec(indices, b);
${broadcastC}
float value = 0.0;
for (int k=0; k<${sharedDim}; ++k) {
a[${rank - 1}] = k;
b[${rank - 2}] = k;
${line}
}
value = value * alpha;
${calculateC}
return value;
}`;
return Object.assign(Object.assign({}, metadata), { output: { dims: outputShape, type: inputs[0].type, textureType: types_1.TextureType.unpacked }, variables: [
{ name: 'alpha', type: 'float', data: attributes.alpha }, { name: 'beta', type: 'float', data: attributes.beta }
], shaderSource });
};
const validateInputs = (inputs, attributes) => {
if (!inputs) {
throw new Error('Input is missing');
}
if (attributes.isOptionalC && (inputs.length < 2 || inputs.length > 3)) {
throw new Error('Invaid input shape.');
}
if (!attributes.isOptionalC && inputs.length !== 3) {
throw new Error('Gemm requires 3 inputs');
}
// 'C' can be of dimensionality 1 or 2 only
if (inputs.length === 3 && inputs[2].dims.length !== 1 && inputs[2].dims.length !== 2) {
throw new Error('Invalid input shape of C');
}
if ((inputs[0].type !== 'float32' && inputs[0].type !== 'float64') ||
(inputs[1].type !== 'float32' && inputs[1].type !== 'float64') ||
(inputs.length === 3 && inputs[2].type !== 'float32' && inputs[2].type !== 'float64')) {
throw new Error('Invalid input type.');
}
if ((inputs[0].type !== inputs[1].type) || (inputs.length === 3 && inputs[0].type !== inputs[2].type)) {
throw new Error('Input types are mismatched');
}
};
//# sourceMappingURL=gemm.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"gemm.js","sourceRoot":"","sources":["gemm.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,137 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import {AttributeWithCacheKey, createAttributeWithCacheKey} from '../../../attribute-with-cache-key';
import {Graph} from '../../../graph';
import {OperatorImplementation, OperatorInitialization} from '../../../operators';
import {Tensor} from '../../../tensor';
import {GemmUtil} from '../../../util';
import {WebGLInferenceHandler} from '../inference-handler';
import {ProgramInfo, ProgramInfoLoader, ProgramMetadata, TextureType} from '../types';
export interface GemmAttributes extends AttributeWithCacheKey {
transA: boolean;
transB: boolean;
alpha: number;
beta: number;
isOptionalC: boolean; // in opset 11, C becomes optional
}
export const gemm: OperatorImplementation<GemmAttributes> =
(inferenceHandler: WebGLInferenceHandler, inputs: Tensor[], attributes: GemmAttributes): Tensor[] => {
validateInputs(inputs, attributes);
const output = inferenceHandler.run(createGemmProgramInfoLoader(inputs, attributes), inputs);
return [output];
};
const parseGemmAttributes = (node: Graph.Node, isOptionalC: boolean): GemmAttributes => {
const transA = node.attributes.getInt('transA', 0) !== 0;
const transB = node.attributes.getInt('transB', 0) !== 0;
const alpha = node.attributes.getFloat('alpha', 1.0);
const beta = node.attributes.getFloat('beta', 1.0);
return createAttributeWithCacheKey({transA, transB, alpha, beta, isOptionalC});
};
export const parseGemmAttributesV7: OperatorInitialization<GemmAttributes> = (node: Graph.Node): GemmAttributes =>
parseGemmAttributes(node, false);
export const parseGemmAttributesV11: OperatorInitialization<GemmAttributes> = (node: Graph.Node): GemmAttributes =>
parseGemmAttributes(node, true);
const createGemmProgramInfoLoader = (inputs: Tensor[], attributes: GemmAttributes): ProgramInfoLoader => {
const metadata = {
name: 'Gemm',
inputNames: inputs.length === 3 ? ['A', 'B', 'C'] : ['A', 'B'],
inputTypes: inputs.length === 3 ? [TextureType.unpacked, TextureType.unpacked, TextureType.unpacked] :
[TextureType.unpacked, TextureType.unpacked],
key: attributes.cacheKey
};
return {...metadata, get: () => createGemmProgramInfo(metadata, inputs, attributes)};
};
const createGemmProgramInfo =
(metadata: ProgramMetadata, inputs: Tensor[], attributes: GemmAttributes): ProgramInfo => {
const aShape = inputs[0].dims.slice();
const bShape = inputs[1].dims.slice();
const [M, N] = GemmUtil.getShapeOfGemmResult(
aShape, attributes.transA, bShape, attributes.transB, inputs.length === 3 ? inputs[2].dims : undefined);
const outputShape = [M, N];
if (!outputShape) {
throw new Error('Can\'t use gemm on the given tensors');
}
let sharedDim = aShape[aShape.length - 1];
let line = '';
if (attributes.transA) {
sharedDim = aShape[0];
}
if (attributes.transA && attributes.transB) {
line = 'value += _A_T(a) * _B_T(b);';
} else if (attributes.transA && !attributes.transB) {
line = 'value += _A_T(a) * _B(b);';
} else if (!attributes.transA && attributes.transB) {
line = 'value += _A(a) * _B_T(b);';
} else if (!attributes.transA && !attributes.transB) {
line = 'value += _A(a) * _B(b);';
}
const rank = outputShape.length;
const declareC = inputs.length === 3 ? `int c[${inputs[2].dims.length}];` : '';
const broadcastC = inputs.length === 3 ? 'bcastIndices_C(indices, c);' : '';
const calculateC = inputs.length === 3 ? 'value += beta * _C(c);' : '';
const shaderSource = `
float process(int indices[${rank}]) {
int a[${rank}];
int b[${rank}];
${declareC}
copyVec(indices, a);
copyVec(indices, b);
${broadcastC}
float value = 0.0;
for (int k=0; k<${sharedDim}; ++k) {
a[${rank - 1}] = k;
b[${rank - 2}] = k;
${line}
}
value = value * alpha;
${calculateC}
return value;
}`;
return {
...metadata,
output: {dims: outputShape, type: inputs[0].type, textureType: TextureType.unpacked},
variables: [
{name: 'alpha', type: 'float', data: attributes.alpha}, {name: 'beta', type: 'float', data: attributes.beta}
],
shaderSource
};
};
const validateInputs = (inputs: Tensor[], attributes: GemmAttributes): void => {
if (!inputs) {
throw new Error('Input is missing');
}
if (attributes.isOptionalC && (inputs.length < 2 || inputs.length > 3)) {
throw new Error('Invaid input shape.');
}
if (!attributes.isOptionalC && inputs.length !== 3) {
throw new Error('Gemm requires 3 inputs');
}
// 'C' can be of dimensionality 1 or 2 only
if (inputs.length === 3 && inputs[2].dims.length !== 1 && inputs[2].dims.length !== 2) {
throw new Error('Invalid input shape of C');
}
if ((inputs[0].type !== 'float32' && inputs[0].type !== 'float64') ||
(inputs[1].type !== 'float32' && inputs[1].type !== 'float64') ||
(inputs.length === 3 && inputs[2].type !== 'float32' && inputs[2].type !== 'float64')) {
throw new Error('Invalid input type.');
}
if ((inputs[0].type !== inputs[1].type) || (inputs.length === 3 && inputs[0].type !== inputs[2].type)) {
throw new Error('Input types are mismatched');
}
};

View File

@@ -0,0 +1,75 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.createPackedIm2ColProgramInfoLoader = void 0;
const glsl_source_1 = require("../glsl-source");
const types_1 = require("../types");
const packing_utils_1 = require("./packing-utils");
const createPackedIm2ColProgramMetadata = (cacheHint) => ({
name: 'Im2Col (packed)',
inputNames: ['A'],
inputTypes: [types_1.TextureType.packed],
cacheHint,
});
const createPackedIm2ColProgramInfo = (inferenceHandler, metadata, x, w, outputShape, attributes) => {
const xshape = x.dims;
const wshape = w.dims;
const rowDim = 2;
const colDim = 3;
const rank = outputShape.length;
const im2colShape = [wshape[1] * wshape[2] * wshape[3], outputShape[2] * outputShape[3]];
const kernelSize = wshape[2] * wshape[3];
const unpackChannel = (0, packing_utils_1.unpackFromChannel)();
const glsl = (0, glsl_source_1.getGlsl)(inferenceHandler.session.backend.glContext.version);
let unrolled = '';
for (let row = 0; row <= 1; row++) {
for (let col = 0; col <= 1; col++) {
unrolled += `
blockIndex = rc.x + ${col};
pos = rc.y + ${row};
if(blockIndex < ${im2colShape[1]} && pos < ${im2colShape[0]}) {
offsetY = int(blockIndex / (${outputShape[rank - 1]})) * ${attributes.strides[0]} -
${attributes.pads[0]};
d0 = offsetY + ${attributes.dilations[0]} * (imod(pos, ${kernelSize}) / ${wshape[2]});
if(d0 < ${xshape[rowDim]} && d0 >= 0) {
offsetX = imod(blockIndex, ${outputShape[rank - 1]}) * ${attributes.strides[1]} -
${attributes.pads[1]};
d1 = offsetX + ${attributes.dilations[1]} * imod(imod(pos, ${kernelSize}), ${wshape[2]});
if(d1 < ${xshape[colDim]} && d1 >= 0) {
ch = int(float(pos)/ ${kernelSize}.);
innerDims = vec2(d0, d1);
result[${row * 2 + col}] = getChannel(
getA(0, ch, int(innerDims.x),
int(innerDims.y)), innerDims);
}
}
}
`;
}
}
const shaderSource = `
${unpackChannel}
void main() {
ivec2 rc = getOutputCoords();
vec4 result = vec4(0.0);
int blockIndex, pos, offsetY, d0, offsetX, d1, ch;
vec2 innerDims;
${unrolled}
${glsl.output} = result;
}
`;
return Object.assign(Object.assign({}, metadata), { output: { dims: im2colShape, type: x.type, textureType: types_1.TextureType.packed }, shaderSource, hasMain: true });
};
const createPackedIm2ColProgramInfoLoader = (inferenceHandler, x, w, outputShape, attributes) => {
const metadata = createPackedIm2ColProgramMetadata(attributes.cacheKey);
return Object.assign(Object.assign({}, metadata), { get: () => createPackedIm2ColProgramInfo(inferenceHandler, metadata, x, w, outputShape, attributes) });
};
exports.createPackedIm2ColProgramInfoLoader = createPackedIm2ColProgramInfoLoader;
//# sourceMappingURL=im2col-pack.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"im2col-pack.js","sourceRoot":"","sources":["im2col-pack.ts"],"names":[],"mappings":";AAAA,4DAA4D;AAC5D,kCAAkC;;;AAGlC,gDAAuC;AAEvC,oCAAsF;AAGtF,mDAAkD;AAElD,MAAM,iCAAiC,GAAG,CAAC,SAAiB,EAAE,EAAE,CAAC,CAAC;IAChE,IAAI,EAAE,iBAAiB;IACvB,UAAU,EAAE,CAAC,GAAG,CAAC;IACjB,UAAU,EAAE,CAAC,mBAAW,CAAC,MAAM,CAAC;IAChC,SAAS;CACV,CAAC,CAAC;AAEH,MAAM,6BAA6B,GAC/B,CAAC,gBAAuC,EAAE,QAAyB,EAAE,CAAS,EAAE,CAAS,EACxF,WAA8B,EAAE,UAA0B,EAAe,EAAE;IAC1E,MAAM,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC;IACtB,MAAM,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC;IACtB,MAAM,MAAM,GAAG,CAAC,CAAC;IACjB,MAAM,MAAM,GAAG,CAAC,CAAC;IACjB,MAAM,IAAI,GAAG,WAAW,CAAC,MAAM,CAAC;IAChC,MAAM,WAAW,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;IACzF,MAAM,UAAU,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACzC,MAAM,aAAa,GAAG,IAAA,iCAAiB,GAAE,CAAC;IAC1C,MAAM,IAAI,GAAG,IAAA,qBAAO,EAAC,gBAAgB,CAAC,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IACzE,IAAI,QAAQ,GAAG,EAAE,CAAC;IAElB,KAAK,IAAI,GAAG,GAAG,CAAC,EAAE,GAAG,IAAI,CAAC,EAAE,GAAG,EAAE,EAAE;QACjC,KAAK,IAAI,GAAG,GAAG,CAAC,EAAE,GAAG,IAAI,CAAC,EAAE,GAAG,EAAE,EAAE;YACjC,QAAQ,IAAI;kCACY,GAAG;2BACV,GAAG;;8BAEA,WAAW,CAAC,CAAC,CAAC,aAAa,WAAW,CAAC,CAAC,CAAC;4CAC3B,WAAW,CAAC,IAAI,GAAG,CAAC,CAAC,QAAQ,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;kBAC5E,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;+BACL,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,iBAAiB,UAAU,OAAO,MAAM,CAAC,CAAC,CAAC;;wBAEzE,MAAM,CAAC,MAAM,CAAC;6CACO,WAAW,CAAC,IAAI,GAAG,CAAC,CAAC,OAAO,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;oBAC1E,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;iCACL,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,qBAAqB,UAAU,MAAM,MAAM,CAAC,CAAC,CAAC;;0BAE5E,MAAM,CAAC,MAAM,CAAC;;yCAEC,UAAU;;6BAEtB,GAAG,GAAG,CAAC,GAAG,GAAG;;;;;;;WAO/B,CAAC;SACH;KACF;IAED,MAAM,YAAY,GAAG;QACnB,aAAa;;;;;;;YAOT,QAAQ;YACR,IAAI,CAAC,MAAM;;aAEV,CAAC;IACR,uCACK,QAAQ,KACX,MAAM,EAAE,EAAC,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,WAAW,EAAE,mBAAW,CAAC,MAAM,EAAC,EAC1E,YAAY,EACZ,OAAO,EAAE,IAAI,IACb;AACJ,CAAC,CAAC;AAEC,MAAM,mCAAmC,GAC5C,CAAC,gBAAuC,EAAE,CAAS,EAAE,CAAS,EAAE,WAA8B,EAC7F,UAA0B,EAAqB,EAAE;IAChD,MAAM,QAAQ,GAAG,iCAAiC,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;IACxE,uCACK,QAAQ,KACX,GAAG,EAAE,GAAG,EAAE,CAAC,6BAA6B,CAAC,gBAAgB,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,EAAE,WAAW,EAAE,UAAU,CAAC,IACnG;AACJ,CAAC,CAAC;AARO,QAAA,mCAAmC,uCAQ1C"}

View File

@@ -0,0 +1,92 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import {Tensor} from '../../../tensor';
import {getGlsl} from '../glsl-source';
import {WebGLInferenceHandler} from '../inference-handler';
import {ProgramInfo, ProgramInfoLoader, ProgramMetadata, TextureType} from '../types';
import {ConvAttributes} from './conv';
import {unpackFromChannel} from './packing-utils';
const createPackedIm2ColProgramMetadata = (cacheHint: string) => ({
name: 'Im2Col (packed)',
inputNames: ['A'],
inputTypes: [TextureType.packed],
cacheHint,
});
const createPackedIm2ColProgramInfo =
(inferenceHandler: WebGLInferenceHandler, metadata: ProgramMetadata, x: Tensor, w: Tensor,
outputShape: readonly number[], attributes: ConvAttributes): ProgramInfo => {
const xshape = x.dims;
const wshape = w.dims;
const rowDim = 2;
const colDim = 3;
const rank = outputShape.length;
const im2colShape = [wshape[1] * wshape[2] * wshape[3], outputShape[2] * outputShape[3]];
const kernelSize = wshape[2] * wshape[3];
const unpackChannel = unpackFromChannel();
const glsl = getGlsl(inferenceHandler.session.backend.glContext.version);
let unrolled = '';
for (let row = 0; row <= 1; row++) {
for (let col = 0; col <= 1; col++) {
unrolled += `
blockIndex = rc.x + ${col};
pos = rc.y + ${row};
if(blockIndex < ${im2colShape[1]} && pos < ${im2colShape[0]}) {
offsetY = int(blockIndex / (${outputShape[rank - 1]})) * ${attributes.strides[0]} -
${attributes.pads[0]};
d0 = offsetY + ${attributes.dilations[0]} * (imod(pos, ${kernelSize}) / ${wshape[2]});
if(d0 < ${xshape[rowDim]} && d0 >= 0) {
offsetX = imod(blockIndex, ${outputShape[rank - 1]}) * ${attributes.strides[1]} -
${attributes.pads[1]};
d1 = offsetX + ${attributes.dilations[1]} * imod(imod(pos, ${kernelSize}), ${wshape[2]});
if(d1 < ${xshape[colDim]} && d1 >= 0) {
ch = int(float(pos)/ ${kernelSize}.);
innerDims = vec2(d0, d1);
result[${row * 2 + col}] = getChannel(
getA(0, ch, int(innerDims.x),
int(innerDims.y)), innerDims);
}
}
}
`;
}
}
const shaderSource = `
${unpackChannel}
void main() {
ivec2 rc = getOutputCoords();
vec4 result = vec4(0.0);
int blockIndex, pos, offsetY, d0, offsetX, d1, ch;
vec2 innerDims;
${unrolled}
${glsl.output} = result;
}
`;
return {
...metadata,
output: {dims: im2colShape, type: x.type, textureType: TextureType.packed},
shaderSource,
hasMain: true
};
};
export const createPackedIm2ColProgramInfoLoader =
(inferenceHandler: WebGLInferenceHandler, x: Tensor, w: Tensor, outputShape: readonly number[],
attributes: ConvAttributes): ProgramInfoLoader => {
const metadata = createPackedIm2ColProgramMetadata(attributes.cacheKey);
return {
...metadata,
get: () => createPackedIm2ColProgramInfo(inferenceHandler, metadata, x, w, outputShape, attributes)
};
};

View File

@@ -0,0 +1,73 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.calculateIm2ColDims = exports.createIm2ColProgramInfoLoader = void 0;
const types_1 = require("../types");
const createIm2ColProgramMetadata = (cacheHint) => ({
name: 'Im2Col',
inputNames: ['X'],
inputTypes: [types_1.TextureType.unpacked],
cacheHint,
});
const createIm2ColProgramInfo = (inferenceHandler, metadata, x, w, outputShape, attributes) => {
const xshape = x.dims;
const wshape = w.dims;
const rank = outputShape.length;
const im2colDims = (0, exports.calculateIm2ColDims)(xshape, wshape, outputShape, 4);
const shaderSource = `
const int XC = ${xshape[1]};
const int XH = ${xshape[2]};
const int XW = ${xshape[3]};
const int KH = ${attributes.kernelShape[0]};
const int KW = ${attributes.kernelShape[1]};
const int dilationH = ${attributes.dilations[0]};
const int dilationW = ${attributes.dilations[1]};
const int strideH = ${attributes.strides[0]};
const int strideW = ${attributes.strides[1]};
const int padH = ${attributes.pads[0]};
const int padW = ${attributes.pads[1]};
const int KHKW = KH*KW;
const int XCKHKW = XC * KHKW;
const int outputChannels = 4;
vec4 process(int indices[${rank}]) {
int b = indices[0]; // batch size
int oh = indices[1] * strideH - padH; //output height
int ow = indices[2] * strideW - padW; //output width
int p = indices[3] * outputChannels; //patch
vec4 value = vec4(0.0);
for(int i=0; i < outputChannels; ++i) {
if(p < XCKHKW) {
int patchC = p / KHKW;
int patchH = (p - patchC*KHKW) / KW;
int patchW = (p - patchC*KHKW) - patchH * KW;
int xh2 = oh + patchH * dilationH;
int xw2 = ow + patchW * dilationW;
int x[${xshape.length}];
x[0] = b;
x[1] = patchC;
x[2] = xh2;
x[3] = xw2;
if(xh2 >= 0 &&
xh2 < XH &&
xw2 >= 0 &&
xw2 < XW) {
value[i] = _X(x);
}
}
++p;
}
return value;
}
`;
return Object.assign(Object.assign({}, metadata), { output: { dims: im2colDims, type: x.type, textureType: types_1.TextureType.packedLastDimension }, shaderSource });
};
const createIm2ColProgramInfoLoader = (inferenceHandler, x, w, outputShape, attributes) => {
const metadata = createIm2ColProgramMetadata(attributes.cacheKey);
return Object.assign(Object.assign({}, metadata), { get: () => createIm2ColProgramInfo(inferenceHandler, metadata, x, w, outputShape, attributes) });
};
exports.createIm2ColProgramInfoLoader = createIm2ColProgramInfoLoader;
const calculateIm2ColDims = (inputShape, kernelShape, outputShape, channels = 4) => [outputShape[0], outputShape[2], outputShape[3],
Math.ceil(inputShape[1] * kernelShape[2] * kernelShape[3] / channels)];
exports.calculateIm2ColDims = calculateIm2ColDims;
//# sourceMappingURL=im2col.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"im2col.js","sourceRoot":"","sources":["im2col.ts"],"names":[],"mappings":";AAAA,4DAA4D;AAC5D,kCAAkC;;;AAIlC,oCAAsF;AAItF,MAAM,2BAA2B,GAAG,CAAC,SAAiB,EAAE,EAAE,CAAC,CAAC;IAC1D,IAAI,EAAE,QAAQ;IACd,UAAU,EAAE,CAAC,GAAG,CAAC;IACjB,UAAU,EAAE,CAAC,mBAAW,CAAC,QAAQ,CAAC;IAClC,SAAS;CACV,CAAC,CAAC;AAEH,MAAM,uBAAuB,GACzB,CAAC,gBAAuC,EAAE,QAAyB,EAAE,CAAS,EAAE,CAAS,EACxF,WAA8B,EAAE,UAA0B,EAAe,EAAE;IAC1E,MAAM,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC;IACtB,MAAM,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC;IAEtB,MAAM,IAAI,GAAG,WAAW,CAAC,MAAM,CAAC;IAChC,MAAM,UAAU,GAAG,IAAA,2BAAmB,EAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC;IAEvE,MAAM,YAAY,GAAG;yBACF,MAAM,CAAC,CAAC,CAAC;yBACT,MAAM,CAAC,CAAC,CAAC;yBACT,MAAM,CAAC,CAAC,CAAC;yBACT,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC;yBACzB,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC;gCAClB,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC;gCACvB,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC;8BACzB,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;8BACrB,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;2BACxB,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;2BAClB,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;;;;mCAIV,IAAI;;;;;;;;;;;;;sBAajB,MAAM,CAAC,MAAM;;;;;;;;;;;;;;;;SAgB1B,CAAC;IACJ,uCACK,QAAQ,KACX,MAAM,EAAE,EAAC,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,WAAW,EAAE,mBAAW,CAAC,mBAAmB,EAAC,EACtF,YAAY,IACZ;AACJ,CAAC,CAAC;AAEC,MAAM,6BAA6B,GACtC,CAAC,gBAAuC,EAAE,CAAS,EAAE,CAAS,EAAE,WAA8B,EAC7F,UAA0B,EAAqB,EAAE;IAChD,MAAM,QAAQ,GAAG,2BAA2B,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;IAClE,uCACK,QAAQ,KACX,GAAG,EAAE,GAAG,EAAE,CAAC,uBAAuB,CAAC,gBAAgB,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,EAAE,WAAW,EAAE,UAAU,CAAC,IAC7F;AACJ,CAAC,CAAC;AARO,QAAA,6BAA6B,iCAQpC;AAGC,MAAM,mBAAmB,GAC5B,CAAC,UAA6B,EAAE,WAA8B,EAAE,WAA8B,EAAE,QAAQ,GAAG,CAAC,EAC/F,EAAE,CACP,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC;IAC9C,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC;AAJvE,QAAA,mBAAmB,uBAIoD"}

View File

@@ -0,0 +1,93 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import {Tensor} from '../../../tensor';
import {WebGLInferenceHandler} from '../inference-handler';
import {ProgramInfo, ProgramInfoLoader, ProgramMetadata, TextureType} from '../types';
import {ConvAttributes} from './conv';
const createIm2ColProgramMetadata = (cacheHint: string) => ({
name: 'Im2Col',
inputNames: ['X'],
inputTypes: [TextureType.unpacked],
cacheHint,
});
const createIm2ColProgramInfo =
(inferenceHandler: WebGLInferenceHandler, metadata: ProgramMetadata, x: Tensor, w: Tensor,
outputShape: readonly number[], attributes: ConvAttributes): ProgramInfo => {
const xshape = x.dims;
const wshape = w.dims;
const rank = outputShape.length;
const im2colDims = calculateIm2ColDims(xshape, wshape, outputShape, 4);
const shaderSource = `
const int XC = ${xshape[1]};
const int XH = ${xshape[2]};
const int XW = ${xshape[3]};
const int KH = ${attributes.kernelShape[0]};
const int KW = ${attributes.kernelShape[1]};
const int dilationH = ${attributes.dilations[0]};
const int dilationW = ${attributes.dilations[1]};
const int strideH = ${attributes.strides[0]};
const int strideW = ${attributes.strides[1]};
const int padH = ${attributes.pads[0]};
const int padW = ${attributes.pads[1]};
const int KHKW = KH*KW;
const int XCKHKW = XC * KHKW;
const int outputChannels = 4;
vec4 process(int indices[${rank}]) {
int b = indices[0]; // batch size
int oh = indices[1] * strideH - padH; //output height
int ow = indices[2] * strideW - padW; //output width
int p = indices[3] * outputChannels; //patch
vec4 value = vec4(0.0);
for(int i=0; i < outputChannels; ++i) {
if(p < XCKHKW) {
int patchC = p / KHKW;
int patchH = (p - patchC*KHKW) / KW;
int patchW = (p - patchC*KHKW) - patchH * KW;
int xh2 = oh + patchH * dilationH;
int xw2 = ow + patchW * dilationW;
int x[${xshape.length}];
x[0] = b;
x[1] = patchC;
x[2] = xh2;
x[3] = xw2;
if(xh2 >= 0 &&
xh2 < XH &&
xw2 >= 0 &&
xw2 < XW) {
value[i] = _X(x);
}
}
++p;
}
return value;
}
`;
return {
...metadata,
output: {dims: im2colDims, type: x.type, textureType: TextureType.packedLastDimension},
shaderSource
};
};
export const createIm2ColProgramInfoLoader =
(inferenceHandler: WebGLInferenceHandler, x: Tensor, w: Tensor, outputShape: readonly number[],
attributes: ConvAttributes): ProgramInfoLoader => {
const metadata = createIm2ColProgramMetadata(attributes.cacheKey);
return {
...metadata,
get: () => createIm2ColProgramInfo(inferenceHandler, metadata, x, w, outputShape, attributes)
};
};
export const calculateIm2ColDims =
(inputShape: readonly number[], kernelShape: readonly number[], outputShape: readonly number[], channels = 4):
number[] =>
[outputShape[0], outputShape[2], outputShape[3],
Math.ceil(inputShape[1] * kernelShape[2] * kernelShape[3] / channels)];

View File

@@ -0,0 +1,74 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseImageScalerAttributes = exports.imageScaler = void 0;
const attribute_with_cache_key_1 = require("../../../attribute-with-cache-key");
const types_1 = require("../types");
const imageScaler = (inferenceHandler, inputs, attributes) => {
validateInputs(inputs);
const output = inferenceHandler.run(createImageScalerProgramInfoLoader(inferenceHandler, inputs, attributes), inputs);
return [output];
};
exports.imageScaler = imageScaler;
const parseImageScalerAttributes = (node) => {
const scale = node.attributes.getFloat('scale');
const bias = node.attributes.getFloats('bias');
return (0, attribute_with_cache_key_1.createAttributeWithCacheKey)({ scale, bias });
};
exports.parseImageScalerAttributes = parseImageScalerAttributes;
const imageScalerProgramMetadata = {
name: 'ImageScaler',
inputNames: ['X'],
inputTypes: [types_1.TextureType.unpacked],
};
const createImageScalerProgramInfo = (handler, metadata, inputs, attributes) => {
const outputShape = inputs[0].dims.slice();
const rank = outputShape.length;
const getBiasMethod = createGetBiasMethod(attributes.bias.length);
const shaderSource = `
${getBiasMethod}
float process(int indices[${rank}]) {
return _X(indices) * scale + getBias(bias, indices[1]);
}`;
return Object.assign(Object.assign({}, metadata), { output: { dims: outputShape, type: inputs[0].type, textureType: types_1.TextureType.unpacked }, variables: [
{ name: 'bias', type: 'float', arrayLength: attributes.bias.length, data: attributes.bias },
{ name: 'scale', type: 'float', data: attributes.scale }
], shaderSource });
};
const createImageScalerProgramInfoLoader = (handler, inputs, attributes) => {
const metadata = Object.assign(Object.assign({}, imageScalerProgramMetadata), { cacheHint: attributes.cacheKey });
return Object.assign(Object.assign({}, metadata), { get: () => createImageScalerProgramInfo(handler, metadata, inputs, attributes) });
};
const createGetBiasMethod = (numChannels) => {
const codeLines = [`float getBias(float bias[${numChannels}], int channel) {`];
for (let i = 0; i < numChannels; ++i) {
if (i === 0) {
codeLines.push('\t' +
`if (channel == ${i}) { return bias[${i}]; }`);
}
else if (i === numChannels - 1) {
codeLines.push('\t' +
`else { return bias[${i}]; }`);
}
else {
codeLines.push('\t' +
`else if (channel == ${i}) { return bias[${i}]; }`);
}
}
codeLines.push('\t' +
'}');
return codeLines.join('\n');
};
const validateInputs = (inputs) => {
if (!inputs || inputs.length !== 1) {
throw new Error('ImageScaler requires 1 input.');
}
if (inputs[0].dims.length !== 4) {
throw new Error('Invalid input shape.');
}
if (inputs[0].type !== 'float32' && inputs[0].type !== 'float64') {
throw new Error('Invalid input type.');
}
};
//# sourceMappingURL=image-scaler.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"image-scaler.js","sourceRoot":"","sources":["image-scaler.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,98 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import {AttributeWithCacheKey, createAttributeWithCacheKey} from '../../../attribute-with-cache-key';
import {Graph} from '../../../graph';
import {OperatorImplementation, OperatorInitialization} from '../../../operators';
import {Tensor} from '../../../tensor';
import {WebGLInferenceHandler} from '../inference-handler';
import {ProgramInfo, ProgramInfoLoader, ProgramMetadata, TextureType} from '../types';
export interface ImageScalerAttributes extends AttributeWithCacheKey {
scale: number;
bias: number[];
}
export const imageScaler: OperatorImplementation<ImageScalerAttributes> =
(inferenceHandler: WebGLInferenceHandler, inputs: Tensor[], attributes: ImageScalerAttributes): Tensor[] => {
validateInputs(inputs);
const output =
inferenceHandler.run(createImageScalerProgramInfoLoader(inferenceHandler, inputs, attributes), inputs);
return [output];
};
export const parseImageScalerAttributes: OperatorInitialization<ImageScalerAttributes> =
(node: Graph.Node): ImageScalerAttributes => {
const scale = node.attributes.getFloat('scale');
const bias = node.attributes.getFloats('bias');
return createAttributeWithCacheKey({scale, bias});
};
const imageScalerProgramMetadata = {
name: 'ImageScaler',
inputNames: ['X'],
inputTypes: [TextureType.unpacked],
};
const createImageScalerProgramInfo =
(handler: WebGLInferenceHandler, metadata: ProgramMetadata, inputs: Tensor[], attributes: ImageScalerAttributes):
ProgramInfo => {
const outputShape = inputs[0].dims.slice();
const rank = outputShape.length;
const getBiasMethod = createGetBiasMethod(attributes.bias.length);
const shaderSource = `
${getBiasMethod}
float process(int indices[${rank}]) {
return _X(indices) * scale + getBias(bias, indices[1]);
}`;
return {
...metadata,
output: {dims: outputShape, type: inputs[0].type, textureType: TextureType.unpacked},
variables: [
{name: 'bias', type: 'float', arrayLength: attributes.bias.length, data: attributes.bias},
{name: 'scale', type: 'float', data: attributes.scale}
],
shaderSource
};
};
const createImageScalerProgramInfoLoader =
(handler: WebGLInferenceHandler, inputs: Tensor[], attributes: ImageScalerAttributes): ProgramInfoLoader => {
const metadata = {...imageScalerProgramMetadata, cacheHint: attributes.cacheKey};
return {...metadata, get: () => createImageScalerProgramInfo(handler, metadata, inputs, attributes)};
};
const createGetBiasMethod = (numChannels: number): string => {
const codeLines: string[] = [`float getBias(float bias[${numChannels}], int channel) {`];
for (let i = 0; i < numChannels; ++i) {
if (i === 0) {
codeLines.push(
'\t' +
`if (channel == ${i}) { return bias[${i}]; }`);
} else if (i === numChannels - 1) {
codeLines.push(
'\t' +
`else { return bias[${i}]; }`);
} else {
codeLines.push(
'\t' +
`else if (channel == ${i}) { return bias[${i}]; }`);
}
}
codeLines.push(
'\t' +
'}');
return codeLines.join('\n');
};
const validateInputs = (inputs: Tensor[]): void => {
if (!inputs || inputs.length !== 1) {
throw new Error('ImageScaler requires 1 input.');
}
if (inputs[0].dims.length !== 4) {
throw new Error('Invalid input shape.');
}
if (inputs[0].type !== 'float32' && inputs[0].type !== 'float64') {
throw new Error('Invalid input type.');
}
};

View File

@@ -0,0 +1,120 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseInstanceNormalizationAttributes = exports.instanceNormalization = void 0;
const glsl_source_1 = require("../glsl-source");
const types_1 = require("../types");
const instanceNormalization = (inferenceHandler, inputs, epsilon) => {
validateInputs(inputs);
const meanAndVariance = inferenceHandler.run(createMeanAndVarianceProgramInfoLoader(inputs[0]), inputs);
const output = inferenceHandler.run(createComputeOutputProgramInfoLoader(inferenceHandler, inputs[0], epsilon, meanAndVariance.dims), [inputs[0], meanAndVariance, inputs[1], inputs[2]]);
return [output];
};
exports.instanceNormalization = instanceNormalization;
const parseInstanceNormalizationAttributes = (node) => node.attributes.getFloat('epsilon', 1e-5);
exports.parseInstanceNormalizationAttributes = parseInstanceNormalizationAttributes;
const meanAndVarianceProgramMetadata = {
name: 'InstanceNormalization_MeanAndVariance',
inputNames: ['X'],
inputTypes: [types_1.TextureType.unpacked],
};
const createMeanAndVarianceProgramInfo = (metadata, input) => {
const xDims = input.dims.slice();
const channel = xDims[1];
const channelSize = xDims[2] * xDims[3];
const outputShape = [xDims[0], channel];
const shaderSource = `
vec4 process(int[2] indices) {
vec4 v = vec4(0.0);
int a[4];
a[0] = indices[0];
a[1] = indices[1];
float temp = 0.0;
for(int a2=0; a2<${xDims[2]}; a2++) {
a[2] = a2;
for(int a3=0; a3<${xDims[3]}; a3++) {
a[3] = a3;
float x = _X(a);
temp += x;
}
}
float mean = temp / float(${channelSize});
temp = 0.0;
for(int a2=0; a2<${xDims[2]}; a2++) {
a[2] = a2;
for(int a3=0; a3<${xDims[3]}; a3++) {
a[3] = a3;
float x = _X(a);
temp += (x - mean) * (x - mean);
}
}
v.r = mean;
v.g = temp / float(${channelSize});
return v;
}`;
return Object.assign(Object.assign({}, metadata), { output: { dims: outputShape, type: input.type, textureType: types_1.TextureType.packedLastDimension }, shaderSource });
};
const createMeanAndVarianceProgramInfoLoader = (input) => (Object.assign(Object.assign({}, meanAndVarianceProgramMetadata), { get: () => createMeanAndVarianceProgramInfo(meanAndVarianceProgramMetadata, input) }));
const computeOutputProgramMetadata = {
name: 'InstanceNormalization_ComputeOutput',
inputNames: ['X', 'MeanAndVariance', 'Scale', 'B'],
inputTypes: [types_1.TextureType.unpacked, types_1.TextureType.packedLastDimension, types_1.TextureType.unpacked, types_1.TextureType.unpacked],
};
const createComputeOutputProgramInfo = (inferenceHandler, metadata, input, epsilon, meanAndVarianceShape) => {
const glsl = (0, glsl_source_1.getGlsl)(inferenceHandler.session.backend.glContext.version);
const [textureWidth, textureHeight] = inferenceHandler.calculateTextureWidthAndHeight(meanAndVarianceShape, types_1.TextureType.packedLastDimension);
const [meanAndVarianceWidth, meanAndVarianceHeight] = [textureWidth / 4, textureHeight];
const shaderSource = `
vec4 get_MeanAndVariance(int[2] mv) {
int offset = indicesToOffset_MeanAndVariance(mv);
vec2 coords = offsetToCoords(offset, ${meanAndVarianceWidth}, ${meanAndVarianceHeight});
return ${glsl.texture2D}(MeanAndVariance, coords);
}
float process(int[4] indices) {
int mv[2];
mv[0] = indices[0];
mv[1] = indices[1];
vec4 mean_and_variance = get_MeanAndVariance(mv);
float mean = mean_and_variance.r;
float variance = mean_and_variance.g;
int sb[1];
sb[0] = indices[1];
float scale = _Scale(sb);
float b = _B(sb);
return scale * (_X(indices) - mean) / sqrt(variance + epsilon) + b;
}`;
return Object.assign(Object.assign({}, metadata), { output: { dims: input.dims, type: input.type, textureType: types_1.TextureType.unpacked }, variables: [{ name: 'epsilon', type: 'float', data: epsilon }], shaderSource });
};
const createComputeOutputProgramInfoLoader = (inferenceHandler, input, epsilon, meanAndVarianceShape) => {
const metadata = Object.assign(Object.assign({}, computeOutputProgramMetadata), { cacheHint: `${epsilon}` });
return Object.assign(Object.assign({}, metadata), { get: () => createComputeOutputProgramInfo(inferenceHandler, metadata, input, epsilon, meanAndVarianceShape) });
};
const validateInputs = (inputs) => {
if (!inputs || inputs.length !== 3) {
throw new Error('InstanceNormalization requires 3 inputs.');
}
const X = inputs[0];
const scale = inputs[1];
const B = inputs[2];
// input should at least have three dimensions - N,C,dim1,...,dimn
// other inputs can have only one dimensions
if (X.dims.length < 3 || scale.dims.length !== 1 || B.dims.length !== 1) {
throw new Error('Invalid input shape.');
}
if (scale.dims[0] !== X.dims[1] || B.dims[0] !== X.dims[1]) {
throw new Error('Input shapes are mismatched.');
}
if ((X.type !== 'float32' && X.type !== 'float64') || (scale.type !== 'float32' && scale.type !== 'float64') ||
(B.type !== 'float32' && B.type !== 'float64')) {
throw new Error('Invalid input type.');
}
if (inputs[0].dims.length !== 4) {
throw new Error('Only support 4-D input shape.');
}
};
//# sourceMappingURL=instance-normalization.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"instance-normalization.js","sourceRoot":"","sources":["instance-normalization.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,156 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import {Graph} from '../../../graph';
import {OperatorImplementation, OperatorInitialization} from '../../../operators';
import {Tensor} from '../../../tensor';
import {getGlsl} from '../glsl-source';
import {WebGLInferenceHandler} from '../inference-handler';
import {ProgramInfo, ProgramInfoLoader, ProgramMetadata, TextureType} from '../types';
export const instanceNormalization: OperatorImplementation<number> =
(inferenceHandler: WebGLInferenceHandler, inputs: Tensor[], epsilon: number): Tensor[] => {
validateInputs(inputs);
const meanAndVariance = inferenceHandler.run(createMeanAndVarianceProgramInfoLoader(inputs[0]), inputs);
const output = inferenceHandler.run(
createComputeOutputProgramInfoLoader(inferenceHandler, inputs[0], epsilon, meanAndVariance.dims),
[inputs[0], meanAndVariance, inputs[1], inputs[2]]);
return [output];
};
export const parseInstanceNormalizationAttributes: OperatorInitialization<number> = (node: Graph.Node): number =>
node.attributes.getFloat('epsilon', 1e-5);
const meanAndVarianceProgramMetadata = {
name: 'InstanceNormalization_MeanAndVariance',
inputNames: ['X'],
inputTypes: [TextureType.unpacked],
};
const createMeanAndVarianceProgramInfo = (metadata: ProgramMetadata, input: Tensor): ProgramInfo => {
const xDims = input.dims.slice();
const channel = xDims[1];
const channelSize = xDims[2] * xDims[3];
const outputShape = [xDims[0], channel];
const shaderSource = `
vec4 process(int[2] indices) {
vec4 v = vec4(0.0);
int a[4];
a[0] = indices[0];
a[1] = indices[1];
float temp = 0.0;
for(int a2=0; a2<${xDims[2]}; a2++) {
a[2] = a2;
for(int a3=0; a3<${xDims[3]}; a3++) {
a[3] = a3;
float x = _X(a);
temp += x;
}
}
float mean = temp / float(${channelSize});
temp = 0.0;
for(int a2=0; a2<${xDims[2]}; a2++) {
a[2] = a2;
for(int a3=0; a3<${xDims[3]}; a3++) {
a[3] = a3;
float x = _X(a);
temp += (x - mean) * (x - mean);
}
}
v.r = mean;
v.g = temp / float(${channelSize});
return v;
}`;
return {
...metadata,
output: {dims: outputShape, type: input.type, textureType: TextureType.packedLastDimension},
shaderSource
};
};
const createMeanAndVarianceProgramInfoLoader = (input: Tensor): ProgramInfoLoader => ({
...meanAndVarianceProgramMetadata,
get: () => createMeanAndVarianceProgramInfo(meanAndVarianceProgramMetadata, input)
});
const computeOutputProgramMetadata = {
name: 'InstanceNormalization_ComputeOutput',
inputNames: ['X', 'MeanAndVariance', 'Scale', 'B'],
inputTypes: [TextureType.unpacked, TextureType.packedLastDimension, TextureType.unpacked, TextureType.unpacked],
};
const createComputeOutputProgramInfo =
(inferenceHandler: WebGLInferenceHandler, metadata: ProgramMetadata, input: Tensor, epsilon: number,
meanAndVarianceShape: readonly number[]): ProgramInfo => {
const glsl = getGlsl(inferenceHandler.session.backend.glContext.version);
const [textureWidth, textureHeight] =
inferenceHandler.calculateTextureWidthAndHeight(meanAndVarianceShape, TextureType.packedLastDimension);
const [meanAndVarianceWidth, meanAndVarianceHeight] = [textureWidth / 4, textureHeight];
const shaderSource = `
vec4 get_MeanAndVariance(int[2] mv) {
int offset = indicesToOffset_MeanAndVariance(mv);
vec2 coords = offsetToCoords(offset, ${meanAndVarianceWidth}, ${meanAndVarianceHeight});
return ${glsl.texture2D}(MeanAndVariance, coords);
}
float process(int[4] indices) {
int mv[2];
mv[0] = indices[0];
mv[1] = indices[1];
vec4 mean_and_variance = get_MeanAndVariance(mv);
float mean = mean_and_variance.r;
float variance = mean_and_variance.g;
int sb[1];
sb[0] = indices[1];
float scale = _Scale(sb);
float b = _B(sb);
return scale * (_X(indices) - mean) / sqrt(variance + epsilon) + b;
}`;
return {
...metadata,
output: {dims: input.dims, type: input.type, textureType: TextureType.unpacked},
variables: [{name: 'epsilon', type: 'float', data: epsilon}],
shaderSource
};
};
const createComputeOutputProgramInfoLoader =
(inferenceHandler: WebGLInferenceHandler, input: Tensor, epsilon: number, meanAndVarianceShape: readonly number[]):
ProgramInfoLoader => {
const metadata = {...computeOutputProgramMetadata, cacheHint: `${epsilon}`};
return {
...metadata,
get: () => createComputeOutputProgramInfo(inferenceHandler, metadata, input, epsilon, meanAndVarianceShape)
};
};
const validateInputs = (inputs: Tensor[]): void => {
if (!inputs || inputs.length !== 3) {
throw new Error('InstanceNormalization requires 3 inputs.');
}
const X = inputs[0];
const scale = inputs[1];
const B = inputs[2];
// input should at least have three dimensions - N,C,dim1,...,dimn
// other inputs can have only one dimensions
if (X.dims.length < 3 || scale.dims.length !== 1 || B.dims.length !== 1) {
throw new Error('Invalid input shape.');
}
if (scale.dims[0] !== X.dims[1] || B.dims[0] !== X.dims[1]) {
throw new Error('Input shapes are mismatched.');
}
if ((X.type !== 'float32' && X.type !== 'float64') || (scale.type !== 'float32' && scale.type !== 'float64') ||
(B.type !== 'float32' && B.type !== 'float64')) {
throw new Error('Invalid input type.');
}
if (inputs[0].dims.length !== 4) {
throw new Error('Only support 4-D input shape.');
}
};

View File

@@ -0,0 +1,131 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.createPackedMatmulProgramInfoLoader = void 0;
const util_1 = require("../../../util");
const glsl_source_1 = require("../glsl-source");
const types_1 = require("../types");
const utils_1 = require("../utils");
const fuse_utils_1 = require("./fuse-utils");
const matmul_1 = require("./matmul");
const createPackedMatmulProgramMetadata = (hasBias, cacheHint) => ({
name: 'MatMul (packed)',
inputNames: hasBias ? ['A', 'B', 'Bias'] : ['A', 'B'],
inputTypes: hasBias ? [types_1.TextureType.packed, types_1.TextureType.packed, types_1.TextureType.packed] :
[types_1.TextureType.packed, types_1.TextureType.packed],
cacheHint
});
const createPackedMatmulProgramInfo = (inferenceHandler, metadata, inputs, activationAttributes) => {
const hasBias = inputs.length > 2;
const processBias = hasBias ? 'value += getBiasForMatmul();' : '';
const aShape = inputs[0].dims;
const bShape = inputs[1].dims;
const outputShape = util_1.BroadcastUtil.calcShape(aShape, bShape, true);
const isBroadcast = !util_1.ShapeUtil.areEqual(inputs[0].dims, inputs[1].dims);
if (!outputShape) {
throw new Error('Can\'t use matmul on the given tensors');
}
const sharedDim = aShape[aShape.length - 1];
const sharedDimIndex = Math.ceil(sharedDim / 2);
const aRank = aShape.length;
const bRank = bShape.length;
const glsl = (0, glsl_source_1.getGlsl)(inferenceHandler.session.backend.glContext.version);
const coordsDataType = (0, utils_1.getCoordsDataType)(outputShape.length);
const outRank = outputShape.length;
const allGlChannels = (0, utils_1.getGlChannels)();
const { activationFunction, applyActivation } = (0, fuse_utils_1.getActivationSnippet)(activationAttributes);
const getBiasForMatmulSnippet = hasBias ? `${(0, matmul_1.getBiasForMatmul)(coordsDataType, allGlChannels, inputs[2].dims, outputShape, true)}` : '';
const getBcastedSamplerForMatmulSnippet = isBroadcast ? `${getBcastSamplerForMatmul(coordsDataType, allGlChannels, inputs, outputShape)}` : '';
const getSamplerAInLoopSnippet = isBroadcast ? 'getAAtOutCoordsMatmul(i)' : `getA(${getA(allGlChannels, aRank)})`;
const getSamplerBInLoopSnippet = isBroadcast ? 'getBAtOutCoordsMatmul(i)' : `getB(${getB(allGlChannels, bRank)})`;
const getOutputCoordsSnippet = isBroadcast ? '' : `${coordsDataType} rc =
getOutputCoords(); int lastDim = rc.${allGlChannels[outRank - 1]}; rc.${allGlChannels[outRank - 1]} =
rc.${allGlChannels[outRank - 2]}; rc.${allGlChannels[outRank - 2]} = lastDim;
`;
const shaderSource = `
${getBcastedSamplerForMatmulSnippet}
${getBiasForMatmulSnippet}
${activationFunction}
void main() {
${getOutputCoordsSnippet}
vec4 value = vec4(0);
for (int i = 0; i < ${sharedDimIndex}; i++) {
vec4 a = ${getSamplerAInLoopSnippet};
vec4 b = ${getSamplerBInLoopSnippet};
value += (a.rrbb * b.rgrg);
value += (a.ggaa * b.baba);
}
${processBias}
${applyActivation}
${glsl.output} = value;
}`;
return Object.assign(Object.assign({}, metadata), { output: { dims: outputShape, type: inputs[0].type, textureType: types_1.TextureType.packed }, shaderSource, hasMain: true });
};
const createPackedMatmulProgramInfoLoader = (inferenceHandler, inputs, activationAttributes) => {
const metadata = createPackedMatmulProgramMetadata(inputs.length > 2, activationAttributes.activationCacheKey);
return Object.assign(Object.assign({}, metadata), { get: () => createPackedMatmulProgramInfo(inferenceHandler, metadata, inputs, activationAttributes) });
};
exports.createPackedMatmulProgramInfoLoader = createPackedMatmulProgramInfoLoader;
function getBcastSamplerForMatmul(coordsDataType, allGlChannels, inputs, outShape) {
let unpackedACoordsSnippet = [];
let unpackedBCoordsSnippet = [];
const inAShape = inputs[0].dims;
const inBShape = inputs[1].dims;
const inARank = inAShape.length;
const inBRank = inBShape.length;
const outRank = outShape.length;
const rankADiff = outRank - inARank;
const rankBDiff = outRank - inBRank;
unpackedACoordsSnippet = inAShape.map((s, i) => `coords.${allGlChannels[i + rankADiff]}`);
unpackedACoordsSnippet[inARank - 1] = 'i*2';
unpackedACoordsSnippet.join(', ');
unpackedBCoordsSnippet = inBShape.map((s, i) => `coords.${allGlChannels[i + rankBDiff]}`);
unpackedBCoordsSnippet[inBRank - 2] = 'i*2';
unpackedBCoordsSnippet.join(', ');
const broadcastADims = util_1.BroadcastUtil.getBroadcastDims(inAShape, outShape);
const broadcastBDims = util_1.BroadcastUtil.getBroadcastDims(inBShape, outShape);
const coordsASnippet = broadcastADims.map(d => `coords.${allGlChannels[d + rankADiff]} = 0;`).join('\n');
const coordsBSnippet = broadcastBDims.map(d => `coords.${allGlChannels[d + rankBDiff]} = 0;`).join('\n');
const swapDimSnippet = `int lastDim = coords.${allGlChannels[outRank - 1]};
coords.${allGlChannels[outRank - 1]} = coords.${allGlChannels[outRank - 2]};
coords.${allGlChannels[outRank - 2]} = lastDim;`;
const getBcastSamplerMatmulSource = `
vec4 getAAtOutCoordsMatmul(int i) {
${coordsDataType} coords = getOutputCoords();
${swapDimSnippet}
${coordsASnippet}
vec4 outputValue = getA(${unpackedACoordsSnippet});
return outputValue;
}
vec4 getBAtOutCoordsMatmul(int i) {
${coordsDataType} coords = getOutputCoords();
${swapDimSnippet}
${coordsBSnippet}
vec4 outputValue = getB(${unpackedBCoordsSnippet});
return outputValue;
}`;
return getBcastSamplerMatmulSource;
}
function getA(allGlChannels, rank) {
let res = '';
for (let i = 0; i < rank - 2; i++) {
res += `rc.${allGlChannels[i]}, `;
}
res += `rc.${allGlChannels[rank - 2]}, ` +
'i*2';
return res;
}
function getB(allGlChannels, rank) {
let res = '';
for (let i = 0; i < rank - 2; i++) {
res += `rc.${allGlChannels[i]}, `;
}
res += 'i*2, ' +
`rc.${allGlChannels[rank - 1]}`;
return res;
}
//# sourceMappingURL=matmul-pack.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,164 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import {Tensor} from '../../../tensor';
import {BroadcastUtil, ShapeUtil} from '../../../util';
import {getGlsl} from '../glsl-source';
import {WebGLInferenceHandler} from '../inference-handler';
import {ProgramInfo, ProgramInfoLoader, ProgramMetadata, TextureType} from '../types';
import {getCoordsDataType, getGlChannels} from '../utils';
import {getActivationSnippet, InternalActivationAttributes} from './fuse-utils';
import {getBiasForMatmul} from './matmul';
const createPackedMatmulProgramMetadata = (hasBias: boolean, cacheHint: string) => ({
name: 'MatMul (packed)',
inputNames: hasBias ? ['A', 'B', 'Bias'] : ['A', 'B'],
inputTypes: hasBias ? [TextureType.packed, TextureType.packed, TextureType.packed] :
[TextureType.packed, TextureType.packed],
cacheHint
});
const createPackedMatmulProgramInfo =
(inferenceHandler: WebGLInferenceHandler, metadata: ProgramMetadata, inputs: Tensor[],
activationAttributes: InternalActivationAttributes): ProgramInfo => {
const hasBias = inputs.length > 2;
const processBias = hasBias ? 'value += getBiasForMatmul();' : '';
const aShape = inputs[0].dims;
const bShape = inputs[1].dims;
const outputShape = BroadcastUtil.calcShape(aShape, bShape, true);
const isBroadcast = !ShapeUtil.areEqual(inputs[0].dims, inputs[1].dims);
if (!outputShape) {
throw new Error('Can\'t use matmul on the given tensors');
}
const sharedDim = aShape[aShape.length - 1];
const sharedDimIndex = Math.ceil(sharedDim / 2);
const aRank = aShape.length;
const bRank = bShape.length;
const glsl = getGlsl(inferenceHandler.session.backend.glContext.version);
const coordsDataType = getCoordsDataType(outputShape.length);
const outRank = outputShape.length;
const allGlChannels = getGlChannels();
const {activationFunction, applyActivation} = getActivationSnippet(activationAttributes);
const getBiasForMatmulSnippet =
hasBias ? `${getBiasForMatmul(coordsDataType, allGlChannels, inputs[2].dims, outputShape, true)}` : '';
const getBcastedSamplerForMatmulSnippet =
isBroadcast ? `${getBcastSamplerForMatmul(coordsDataType, allGlChannels, inputs, outputShape)}` : '';
const getSamplerAInLoopSnippet = isBroadcast ? 'getAAtOutCoordsMatmul(i)' : `getA(${getA(allGlChannels, aRank)})`;
const getSamplerBInLoopSnippet = isBroadcast ? 'getBAtOutCoordsMatmul(i)' : `getB(${getB(allGlChannels, bRank)})`;
const getOutputCoordsSnippet = isBroadcast ? '' : `${coordsDataType} rc =
getOutputCoords(); int lastDim = rc.${allGlChannels[outRank - 1]}; rc.${allGlChannels[outRank - 1]} =
rc.${allGlChannels[outRank - 2]}; rc.${allGlChannels[outRank - 2]} = lastDim;
`;
const shaderSource = `
${getBcastedSamplerForMatmulSnippet}
${getBiasForMatmulSnippet}
${activationFunction}
void main() {
${getOutputCoordsSnippet}
vec4 value = vec4(0);
for (int i = 0; i < ${sharedDimIndex}; i++) {
vec4 a = ${getSamplerAInLoopSnippet};
vec4 b = ${getSamplerBInLoopSnippet};
value += (a.rrbb * b.rgrg);
value += (a.ggaa * b.baba);
}
${processBias}
${applyActivation}
${glsl.output} = value;
}`;
return {
...metadata,
output: {dims: outputShape, type: inputs[0].type, textureType: TextureType.packed},
shaderSource,
hasMain: true
};
};
export const createPackedMatmulProgramInfoLoader =
(inferenceHandler: WebGLInferenceHandler, inputs: Tensor[],
activationAttributes: InternalActivationAttributes): ProgramInfoLoader => {
const metadata = createPackedMatmulProgramMetadata(inputs.length > 2, activationAttributes.activationCacheKey);
return {
...metadata,
get: () => createPackedMatmulProgramInfo(inferenceHandler, metadata, inputs, activationAttributes)
};
};
function getBcastSamplerForMatmul(
coordsDataType: string, allGlChannels: readonly string[], inputs: Tensor[], outShape: readonly number[]): string {
let unpackedACoordsSnippet = [];
let unpackedBCoordsSnippet = [];
const inAShape = inputs[0].dims;
const inBShape = inputs[1].dims;
const inARank = inAShape.length;
const inBRank = inBShape.length;
const outRank = outShape.length;
const rankADiff = outRank - inARank;
const rankBDiff = outRank - inBRank;
unpackedACoordsSnippet = inAShape.map((s, i) => `coords.${allGlChannels[i + rankADiff]}`);
unpackedACoordsSnippet[inARank - 1] = 'i*2';
unpackedACoordsSnippet.join(', ');
unpackedBCoordsSnippet = inBShape.map((s, i) => `coords.${allGlChannels[i + rankBDiff]}`);
unpackedBCoordsSnippet[inBRank - 2] = 'i*2';
unpackedBCoordsSnippet.join(', ');
const broadcastADims = BroadcastUtil.getBroadcastDims(inAShape, outShape);
const broadcastBDims = BroadcastUtil.getBroadcastDims(inBShape, outShape);
const coordsASnippet = broadcastADims.map(d => `coords.${allGlChannels[d + rankADiff]} = 0;`).join('\n');
const coordsBSnippet = broadcastBDims.map(d => `coords.${allGlChannels[d + rankBDiff]} = 0;`).join('\n');
const swapDimSnippet = `int lastDim = coords.${allGlChannels[outRank - 1]};
coords.${allGlChannels[outRank - 1]} = coords.${allGlChannels[outRank - 2]};
coords.${allGlChannels[outRank - 2]} = lastDim;`;
const getBcastSamplerMatmulSource = `
vec4 getAAtOutCoordsMatmul(int i) {
${coordsDataType} coords = getOutputCoords();
${swapDimSnippet}
${coordsASnippet}
vec4 outputValue = getA(${unpackedACoordsSnippet});
return outputValue;
}
vec4 getBAtOutCoordsMatmul(int i) {
${coordsDataType} coords = getOutputCoords();
${swapDimSnippet}
${coordsBSnippet}
vec4 outputValue = getB(${unpackedBCoordsSnippet});
return outputValue;
}`;
return getBcastSamplerMatmulSource;
}
function getA(allGlChannels: string[], rank: number): string {
let res = '';
for (let i = 0; i < rank - 2; i++) {
res += `rc.${allGlChannels[i]}, `;
}
res += `rc.${allGlChannels[rank - 2]}, ` +
'i*2';
return res;
}
function getB(allGlChannels: string[], rank: number): string {
let res = '';
for (let i = 0; i < rank - 2; i++) {
res += `rc.${allGlChannels[i]}, `;
}
res += 'i*2, ' +
`rc.${allGlChannels[rank - 1]}`;
return res;
}

View File

@@ -0,0 +1,123 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.getBiasForMatmul = exports.createMatmulProgramInfoLoader = exports.parseMatMulAttributes = exports.matMul = void 0;
const util_1 = require("../../../util");
const types_1 = require("../types");
const utils_1 = require("../utils");
const fuse_utils_1 = require("./fuse-utils");
const matmul_pack_1 = require("./matmul-pack");
const matMul = (inferenceHandler, inputs, attributes) => {
validateInputs(inputs);
if (inferenceHandler.session.pack) {
return [inferenceHandler.run((0, matmul_pack_1.createPackedMatmulProgramInfoLoader)(inferenceHandler, inputs, attributes), inputs)];
}
else {
return [inferenceHandler.run(createMatmulProgramInfoLoader(inputs, attributes), inputs)];
}
};
exports.matMul = matMul;
const parseMatMulAttributes = (node) => (0, fuse_utils_1.parseInternalActivationAttributes)(node.attributes);
exports.parseMatMulAttributes = parseMatMulAttributes;
const createMatmulProgramMetadata = (hasBias, cacheHint) => ({
name: 'MatMul',
inputNames: hasBias ? ['A', 'B', 'Bias'] : ['A', 'B'],
inputTypes: hasBias ? [types_1.TextureType.unpacked, types_1.TextureType.unpacked, types_1.TextureType.unpacked] :
[types_1.TextureType.unpacked, types_1.TextureType.unpacked],
cacheHint
});
function createMatmulProgramInfo(metadata, inputs, activationAttributes) {
const aShape = inputs[0].dims;
const bShape = inputs[1].dims;
const outputShape = util_1.BroadcastUtil.calcShape(aShape, bShape, true);
if (!outputShape) {
throw new Error('Can\'t use matmul on the given tensors');
}
const coordsDataType = (0, utils_1.getCoordsDataType)(outputShape.length);
const allGlChannels = (0, utils_1.getGlChannels)();
const { activationFunction, applyActivation } = (0, fuse_utils_1.getActivationSnippet)(activationAttributes);
const hasBias = inputs.length > 2;
const processBias = hasBias ? 'value += getBiasForMatmul();' : '';
const getBiasForMatmulSnippet = hasBias ? `${getBiasForMatmul(coordsDataType, allGlChannels, inputs[2].dims, outputShape, false)}` : '';
const rank = outputShape.length;
const arank = aShape.length;
const brank = bShape.length;
const sharedDim = aShape[aShape.length - 1];
const shaderSource = `
${activationFunction}
${getBiasForMatmulSnippet}
float process(int indices[${rank}]) {
int a[${arank}];
int b[${brank}];
bcastMatmulIndices_A(indices, a);
bcastMatmulIndices_B(indices, b);
float value;
for (int k=0; k<${sharedDim}; ++k) {
a[${arank - 1}] = k;
b[${brank - 2}] = k;
value += _A(a) * _B(b);
}
${processBias}
${applyActivation}
return value;
}`;
return Object.assign(Object.assign({}, metadata), { output: { dims: outputShape, type: inputs[0].type, textureType: types_1.TextureType.unpacked }, shaderSource });
}
function createMatmulProgramInfoLoader(inputs, activationAttributes) {
const metadata = createMatmulProgramMetadata(inputs.length > 2, activationAttributes.activationCacheKey);
return Object.assign(Object.assign({}, metadata), { get: () => createMatmulProgramInfo(metadata, inputs, activationAttributes) });
}
exports.createMatmulProgramInfoLoader = createMatmulProgramInfoLoader;
const validateInputs = (inputs) => {
if (!inputs || inputs.length !== 2) {
throw new Error('MatMul requires 2 inputs.');
}
if (inputs[0].dims[inputs[0].dims.length - 1] !== inputs[1].dims[inputs[1].dims.length - 2]) {
throw new Error('shared dimension does not match.');
}
if ((inputs[0].type !== 'float32' && inputs[0].type !== 'float64') ||
(inputs[1].type !== 'float32' && inputs[1].type !== 'float64')) {
throw new Error('inputs should be float type');
}
if (inputs[0].type !== inputs[1].type) {
throw new Error('inputs types should match');
}
};
function getBiasForMatmul(coordsDataType, allGlChannels, inShape, outShape, isPacked) {
let unpackedCoordsSnippet = '';
const inRank = inShape.length;
const outRank = outShape.length;
const rankDiff = outRank - inRank;
if (outRank < 2 && inRank > 0) {
unpackedCoordsSnippet = 'coords';
}
else {
unpackedCoordsSnippet = inShape.map((s, i) => `coords.${allGlChannels[i + rankDiff]}`).join(', ');
}
const broadcastDims = util_1.BroadcastUtil.getBroadcastDims(inShape, outShape);
const coordsSnippet = broadcastDims.map(d => `coords.${allGlChannels[d + rankDiff]} = 0;`).join('\n');
const inSize = util_1.ShapeUtil.size(inShape);
const isInputScalar = inSize === 1;
let output = 'vec4(outputValue.xx, outputValue.yy)';
if (isInputScalar) {
output = 'vec4(outputValue.x)';
}
const getBiasForMatmulSource = isPacked ? `
vec4 getBiasForMatmul() {
${coordsDataType} coords = getOutputCoords();
${coordsSnippet}
vec4 outputValue = getBias(${unpackedCoordsSnippet});
return ${output};
}` :
`
float getBiasForMatmul() {
${coordsDataType} coords = getOutputCoords();
${coordsSnippet}
return getBias(coords.x);
}`;
return getBiasForMatmulSource;
}
exports.getBiasForMatmul = getBiasForMatmul;
//# sourceMappingURL=matmul.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"matmul.js","sourceRoot":"","sources":["matmul.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,145 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import {Graph} from '../../../graph';
import {OperatorImplementation, OperatorInitialization} from '../../../operators';
import {Tensor} from '../../../tensor';
import {BroadcastUtil, ShapeUtil} from '../../../util';
import {WebGLInferenceHandler} from '../inference-handler';
import {ProgramInfo, ProgramInfoLoader, ProgramMetadata, TextureType} from '../types';
import {getCoordsDataType, getGlChannels} from '../utils';
import {getActivationSnippet, InternalActivationAttributes, parseInternalActivationAttributes} from './fuse-utils';
import {createPackedMatmulProgramInfoLoader} from './matmul-pack';
export const matMul: OperatorImplementation<InternalActivationAttributes> =
(inferenceHandler: WebGLInferenceHandler, inputs: Tensor[], attributes: InternalActivationAttributes): Tensor[] => {
validateInputs(inputs);
if (inferenceHandler.session.pack) {
return [inferenceHandler.run(
createPackedMatmulProgramInfoLoader(inferenceHandler, inputs, attributes), inputs)];
} else {
return [inferenceHandler.run(createMatmulProgramInfoLoader(inputs, attributes), inputs)];
}
};
export const parseMatMulAttributes: OperatorInitialization<InternalActivationAttributes> =
(node: Graph.Node): InternalActivationAttributes => parseInternalActivationAttributes(node.attributes);
const createMatmulProgramMetadata = (hasBias: boolean, cacheHint: string) => ({
name: 'MatMul',
inputNames: hasBias ? ['A', 'B', 'Bias'] : ['A', 'B'],
inputTypes: hasBias ? [TextureType.unpacked, TextureType.unpacked, TextureType.unpacked] :
[TextureType.unpacked, TextureType.unpacked],
cacheHint
});
function createMatmulProgramInfo(
metadata: ProgramMetadata, inputs: Tensor[], activationAttributes: InternalActivationAttributes): ProgramInfo {
const aShape = inputs[0].dims;
const bShape = inputs[1].dims;
const outputShape = BroadcastUtil.calcShape(aShape, bShape, true);
if (!outputShape) {
throw new Error('Can\'t use matmul on the given tensors');
}
const coordsDataType = getCoordsDataType(outputShape.length);
const allGlChannels = getGlChannels();
const {activationFunction, applyActivation} = getActivationSnippet(activationAttributes);
const hasBias = inputs.length > 2;
const processBias = hasBias ? 'value += getBiasForMatmul();' : '';
const getBiasForMatmulSnippet =
hasBias ? `${getBiasForMatmul(coordsDataType, allGlChannels, inputs[2].dims, outputShape, false)}` : '';
const rank = outputShape.length;
const arank = aShape.length;
const brank = bShape.length;
const sharedDim = aShape[aShape.length - 1];
const shaderSource = `
${activationFunction}
${getBiasForMatmulSnippet}
float process(int indices[${rank}]) {
int a[${arank}];
int b[${brank}];
bcastMatmulIndices_A(indices, a);
bcastMatmulIndices_B(indices, b);
float value;
for (int k=0; k<${sharedDim}; ++k) {
a[${arank - 1}] = k;
b[${brank - 2}] = k;
value += _A(a) * _B(b);
}
${processBias}
${applyActivation}
return value;
}`;
return {
...metadata,
output: {dims: outputShape, type: inputs[0].type, textureType: TextureType.unpacked},
shaderSource,
};
}
export function createMatmulProgramInfoLoader(
inputs: Tensor[], activationAttributes: InternalActivationAttributes): ProgramInfoLoader {
const metadata = createMatmulProgramMetadata(inputs.length > 2, activationAttributes.activationCacheKey);
return {...metadata, get: () => createMatmulProgramInfo(metadata, inputs, activationAttributes)};
}
const validateInputs = (inputs: Tensor[]): void => {
if (!inputs || inputs.length !== 2) {
throw new Error('MatMul requires 2 inputs.');
}
if (inputs[0].dims[inputs[0].dims.length - 1] !== inputs[1].dims[inputs[1].dims.length - 2]) {
throw new Error('shared dimension does not match.');
}
if ((inputs[0].type !== 'float32' && inputs[0].type !== 'float64') ||
(inputs[1].type !== 'float32' && inputs[1].type !== 'float64')) {
throw new Error('inputs should be float type');
}
if (inputs[0].type !== inputs[1].type) {
throw new Error('inputs types should match');
}
};
export function getBiasForMatmul(
coordsDataType: string, allGlChannels: readonly string[], inShape: readonly number[], outShape: readonly number[],
isPacked: boolean): string {
let unpackedCoordsSnippet = '';
const inRank = inShape.length;
const outRank = outShape.length;
const rankDiff = outRank - inRank;
if (outRank < 2 && inRank > 0) {
unpackedCoordsSnippet = 'coords';
} else {
unpackedCoordsSnippet = inShape.map((s, i) => `coords.${allGlChannels[i + rankDiff]}`).join(', ');
}
const broadcastDims = BroadcastUtil.getBroadcastDims(inShape, outShape);
const coordsSnippet = broadcastDims.map(d => `coords.${allGlChannels[d + rankDiff]} = 0;`).join('\n');
const inSize = ShapeUtil.size(inShape);
const isInputScalar = inSize === 1;
let output = 'vec4(outputValue.xx, outputValue.yy)';
if (isInputScalar) {
output = 'vec4(outputValue.x)';
}
const getBiasForMatmulSource = isPacked ? `
vec4 getBiasForMatmul() {
${coordsDataType} coords = getOutputCoords();
${coordsSnippet}
vec4 outputValue = getBias(${unpackedCoordsSnippet});
return ${output};
}` :
`
float getBiasForMatmul() {
${coordsDataType} coords = getOutputCoords();
${coordsSnippet}
return getBias(coords.x);
}`;
return getBiasForMatmulSource;
}

View File

@@ -0,0 +1,120 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.createPackProgramInfoLoader = void 0;
const glsl_source_1 = require("../glsl-source");
const types_1 = require("../types");
const utils_1 = require("../utils");
const packing_utils_1 = require("./packing-utils");
const packProgramMetadata = {
name: 'pack',
inputNames: ['A'],
inputTypes: [types_1.TextureType.unpackedReversed]
};
const createPackProgramInfo = (handler, input) => {
const glsl = (0, glsl_source_1.getGlsl)(handler.session.backend.glContext.version);
const inputShape = input.dims;
const inputRank = inputShape.length;
// createTextureLayoutFromShape won't change output rank. Need to verify by running tests
const outputRank = input.dims.length;
const coordsDataType = (0, utils_1.getCoordsDataType)(outputRank);
const channels = (0, packing_utils_1.getChannels)('rc', outputRank);
const setup = getSetup(outputRank, channels, inputShape[inputShape.length - 2], inputShape[inputShape.length - 1]);
let reversedInputWH;
if (inputRank === 0) {
reversedInputWH = [1, 1];
}
else if (inputRank === 1) {
reversedInputWH = [inputShape[0], 1];
}
else {
reversedInputWH = [inputShape[outputRank - 1], inputShape[outputRank - 2]];
}
const outOfBoundsCondition = getOutOfBoundsCondition(outputRank, reversedInputWH, channels);
const output = getOutput(inputShape, channels);
const shaderSource = `
void main() {
${coordsDataType} rc = getOutputCoords();
if(${outOfBoundsCondition}) {
${glsl.output} = vec4(0);
} else {
${setup}
${glsl.output} = vec4(${output});
}
}
`;
return Object.assign(Object.assign({}, packProgramMetadata), { hasMain: true, output: { dims: input.dims, type: input.type, textureType: types_1.TextureType.packed }, shaderSource });
};
const createPackProgramInfoLoader = (handler, input) => (Object.assign(Object.assign({}, packProgramMetadata), { get: () => createPackProgramInfo(handler, input) }));
exports.createPackProgramInfoLoader = createPackProgramInfoLoader;
/**
* check output coordinate location and return false if it is outside input's width/height boundary
*/
function getOutOfBoundsCondition(rank, shape, dims) {
if (rank === 0) {
return 'false';
}
if (rank === 1) {
return `rc > ${shape[0]}`;
}
let cond = '';
for (let i = rank - 2; i < rank; i++) {
cond += `${dims[i]} >= ${shape[i - rank + 2]}`;
if (i < rank - 1) {
cond += '||';
}
}
return cond;
}
/**
* code snippet to sample input texture with output coordiantes
*/
function getOutput(shape, dims) {
const rank = shape.length;
if (rank === 0) {
return 'getA(), 0, 0, 0';
}
if (rank === 1) {
return `getA(rc),
rc + 1 >= ${shape[0]} ? 0. : getA(rc + 1),
0, 0`;
}
const coord00 = 'r, c';
const coord01 = 'r, cp1';
const coord10 = 'rp1, c';
const coord11 = 'rp1, cp1';
let D = '';
if (rank > 2) {
for (let i = 0; i < rank - 2; ++i) {
D = D + `${dims[i]},`;
}
}
return `getA(${D}${coord00}),
rEdge ? 0. : getA(${D}${coord10}),
cEdge ? 0. : getA(${D}${coord01}),
rEdge || cEdge ? 0. : getA(${D}${coord11})`;
}
/**
* code snippet to setup 4 coordinates and edge conditions
*/
function getSetup(rank, dims, rows, cols) {
if (rank === 0 || rank === 1) {
return '';
}
// rank >= 2 for width+height pack.
else {
const setup = `
int r = ${dims[rank - 2]};
int c = ${dims[rank - 1]};
int rp1 = ${dims[rank - 2]} + 1;
int cp1 = ${dims[rank - 1]} + 1;
bool rEdge = rp1 >= ${cols};
bool cEdge = cp1 >= ${rows};
`;
return setup;
}
}
//# sourceMappingURL=pack.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"pack.js","sourceRoot":"","sources":["pack.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,138 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import {Tensor} from '../../../tensor';
import {getGlsl} from '../glsl-source';
import {WebGLInferenceHandler} from '../inference-handler';
import {ProgramInfo, ProgramInfoLoader, TextureType} from '../types';
import {getCoordsDataType} from '../utils';
import {getChannels} from './packing-utils';
const packProgramMetadata = {
name: 'pack',
inputNames: ['A'],
inputTypes: [TextureType.unpackedReversed]
};
const createPackProgramInfo = (handler: WebGLInferenceHandler, input: Tensor): ProgramInfo => {
const glsl = getGlsl(handler.session.backend.glContext.version);
const inputShape = input.dims;
const inputRank = inputShape.length;
// createTextureLayoutFromShape won't change output rank. Need to verify by running tests
const outputRank = input.dims.length;
const coordsDataType = getCoordsDataType(outputRank);
const channels = getChannels('rc', outputRank);
const setup = getSetup(outputRank, channels, inputShape[inputShape.length - 2], inputShape[inputShape.length - 1]);
let reversedInputWH;
if (inputRank === 0) {
reversedInputWH = [1, 1];
} else if (inputRank === 1) {
reversedInputWH = [inputShape[0], 1];
} else {
reversedInputWH = [inputShape[outputRank - 1], inputShape[outputRank - 2]];
}
const outOfBoundsCondition = getOutOfBoundsCondition(outputRank, reversedInputWH, channels);
const output = getOutput(inputShape, channels);
const shaderSource = `
void main() {
${coordsDataType} rc = getOutputCoords();
if(${outOfBoundsCondition}) {
${glsl.output} = vec4(0);
} else {
${setup}
${glsl.output} = vec4(${output});
}
}
`;
return {
...packProgramMetadata,
hasMain: true,
output: {dims: input.dims, type: input.type, textureType: TextureType.packed},
shaderSource
};
};
export const createPackProgramInfoLoader = (handler: WebGLInferenceHandler, input: Tensor): ProgramInfoLoader =>
({...packProgramMetadata, get: () => createPackProgramInfo(handler, input)});
/**
* check output coordinate location and return false if it is outside input's width/height boundary
*/
function getOutOfBoundsCondition(rank: number, shape: readonly number[], dims: string[]): string {
if (rank === 0) {
return 'false';
}
if (rank === 1) {
return `rc > ${shape[0]}`;
}
let cond = '';
for (let i = rank - 2; i < rank; i++) {
cond += `${dims[i]} >= ${shape[i - rank + 2]}`;
if (i < rank - 1) {
cond += '||';
}
}
return cond;
}
/**
* code snippet to sample input texture with output coordiantes
*/
function getOutput(shape: readonly number[], dims: string[]): string {
const rank = shape.length;
if (rank === 0) {
return 'getA(), 0, 0, 0';
}
if (rank === 1) {
return `getA(rc),
rc + 1 >= ${shape[0]} ? 0. : getA(rc + 1),
0, 0`;
}
const coord00 = 'r, c';
const coord01 = 'r, cp1';
const coord10 = 'rp1, c';
const coord11 = 'rp1, cp1';
let D = '';
if (rank > 2) {
for (let i = 0; i < rank - 2; ++i) {
D = D + `${dims[i]},`;
}
}
return `getA(${D}${coord00}),
rEdge ? 0. : getA(${D}${coord10}),
cEdge ? 0. : getA(${D}${coord01}),
rEdge || cEdge ? 0. : getA(${D}${coord11})`;
}
/**
* code snippet to setup 4 coordinates and edge conditions
*/
function getSetup(rank: number, dims: string[], rows: number, cols: number): string {
if (rank === 0 || rank === 1) {
return '';
}
// rank >= 2 for width+height pack.
else {
const setup = `
int r = ${dims[rank - 2]};
int c = ${dims[rank - 1]};
int rp1 = ${dims[rank - 2]} + 1;
int cp1 = ${dims[rank - 1]} + 1;
bool rEdge = rp1 >= ${cols};
bool cEdge = cp1 >= ${rows};
`;
return setup;
}
}

View File

@@ -0,0 +1,34 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.unpackFromChannel = exports.getChannels = exports.getVecChannels = void 0;
const utils_1 = require("../utils");
function getVecChannels(name, rank) {
return (0, utils_1.getGlChannels)(rank).map(d => `${name}.${d}`);
}
exports.getVecChannels = getVecChannels;
function getChannels(name, rank) {
if (rank === 1) {
return [name];
}
return getVecChannels(name, rank);
}
exports.getChannels = getChannels;
function unpackFromChannel() {
return `
float getChannel(vec4 frag, int dim) {
int modCoord = imod(dim, 2);
return modCoord == 0 ? frag.r : frag.g;
}
float getChannel(vec4 frag, vec2 innerDims) {
vec2 modCoord = mod(innerDims, 2.);
return modCoord.x == 0. ?
(modCoord.y == 0. ? frag.r : frag.g) :
(modCoord.y == 0. ? frag.b : frag.a);
}
`;
}
exports.unpackFromChannel = unpackFromChannel;
//# sourceMappingURL=packing-utils.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"packing-utils.js","sourceRoot":"","sources":["packing-utils.ts"],"names":[],"mappings":";AAAA,4DAA4D;AAC5D,kCAAkC;;;AAElC,oCAAuC;AAEvC,SAAgB,cAAc,CAAC,IAAY,EAAE,IAAY;IACvD,OAAO,IAAA,qBAAa,EAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC;AACtD,CAAC;AAFD,wCAEC;AAED,SAAgB,WAAW,CAAC,IAAY,EAAE,IAAY;IACpD,IAAI,IAAI,KAAK,CAAC,EAAE;QACd,OAAO,CAAC,IAAI,CAAC,CAAC;KACf;IACD,OAAO,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;AACpC,CAAC;AALD,kCAKC;AAED,SAAgB,iBAAiB;IAC/B,OAAO;;;;;;;;;;;;GAYN,CAAC;AACJ,CAAC;AAdD,8CAcC"}

View File

@@ -0,0 +1,31 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import {getGlChannels} from '../utils';
export function getVecChannels(name: string, rank: number): string[] {
return getGlChannels(rank).map(d => `${name}.${d}`);
}
export function getChannels(name: string, rank: number): string[] {
if (rank === 1) {
return [name];
}
return getVecChannels(name, rank);
}
export function unpackFromChannel(): string {
return `
float getChannel(vec4 frag, int dim) {
int modCoord = imod(dim, 2);
return modCoord == 0 ? frag.r : frag.g;
}
float getChannel(vec4 frag, vec2 innerDims) {
vec2 modCoord = mod(innerDims, 2.);
return modCoord.x == 0. ?
(modCoord.y == 0. ? frag.r : frag.g) :
(modCoord.y == 0. ? frag.b : frag.a);
}
`;
}

View File

@@ -0,0 +1,167 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.parsePadAttributesV11 = exports.padV11 = exports.parsePadAttributesV2 = exports.padV2 = void 0;
const attribute_with_cache_key_1 = require("../../../attribute-with-cache-key");
const util_1 = require("../../../util");
const glsl_source_1 = require("../glsl-source");
const types_1 = require("../types");
const padProgramMetadata = {
name: 'Pad',
inputNames: ['A'],
inputTypes: [types_1.TextureType.unpacked],
};
const padV2 = (inferenceHandler, inputs, attributes) => {
validateInputsV2(inputs);
const output = inferenceHandler.run(Object.assign(Object.assign({}, padProgramMetadata), { cacheHint: attributes.cacheKey, get: () => createPadProgramInfo(inferenceHandler, inputs[0], attributes) }), inputs);
return [output];
};
exports.padV2 = padV2;
const parsePadAttributesV2 = (node) => {
const mode = node.attributes.getString('mode', 'constant');
const value = node.attributes.getFloat('value', 0.0);
const pads = node.attributes.getInts('pads');
return (0, attribute_with_cache_key_1.createAttributeWithCacheKey)({ mode, value, pads });
};
exports.parsePadAttributesV2 = parsePadAttributesV2;
const padV11 = (inferenceHandler, inputs, mode) => {
validateInputsV11(inputs);
const attrubutes = generatePadAttributesFromInputs(inferenceHandler, inputs, mode);
return (0, exports.padV2)(inferenceHandler, [inputs[0]], attrubutes);
};
exports.padV11 = padV11;
const parsePadAttributesV11 = (node) => node.attributes.getString('mode', 'constant');
exports.parsePadAttributesV11 = parsePadAttributesV11;
const generatePadAttributesFromInputs = (inferenceHandler, inputs, mode) => {
if (!inferenceHandler.session.isInitializer(inputs[1].dataId) ||
(inputs.length >= 3 && !inferenceHandler.session.isInitializer(inputs[2].dataId))) {
throw new Error('dynamic pad attributes are not allowed');
}
const pads = Array.from(inputs[1].integerData);
const value = (inputs.length >= 3) ? inputs[2].floatData[0] : 0.0;
return (0, attribute_with_cache_key_1.createAttributeWithCacheKey)({ mode, pads, value });
};
const createPadProgramInfo = (inferenceHandler, input, attributes) => {
const outputShape = util_1.ShapeUtil.padShape(input.dims.slice(), attributes.pads);
const rank = outputShape.length;
const padFunction = getPadFunction(inferenceHandler, input, attributes);
const shaderSource = `
${padFunction}
float process(int[${rank}] indices) {
return padA(indices);
}`;
return {
name: 'Pad',
inputNames: ['A'],
inputTypes: [types_1.TextureType.unpacked],
output: { dims: outputShape, type: input.type, textureType: types_1.TextureType.unpacked },
shaderSource
};
};
const validateInputsV2 = (inputs) => {
if (!inputs || inputs.length !== 1) {
throw new Error('Pad requires 1 input');
}
if (inputs[0].type !== 'float32' && inputs[0].type !== 'float64') {
throw new Error('Invalid input type.');
}
};
const validateInputsV11 = (inputs) => {
if (!inputs || (inputs.length !== 2 && inputs.length !== 3)) {
throw new Error('Pad requires 2 or 3 inputs');
}
if (inputs[1].type !== 'int32') {
throw new Error('Invalid input type.');
}
if (inputs.length >= 3 && inputs[2].type === 'string') {
throw new Error('Invalid input type.');
}
};
const getPadFunction = (inferenceHandler, input, attributes) => {
const glsl = (0, glsl_source_1.getGlsl)(inferenceHandler.session.backend.glContext.version);
const [width, height] = inferenceHandler.calculateTextureWidthAndHeight(input.dims, types_1.TextureType.unpacked);
const strides = util_1.ShapeUtil.computeStrides(input.dims);
switch (attributes.mode) {
case 'constant':
return getPadConstant(glsl, input.dims, strides, width, height, attributes.pads, attributes.value);
case 'reflect':
return getPadReflect(glsl, input.dims, strides, width, height, attributes.pads);
case 'edge':
return getPadEdge(glsl, input.dims, strides, width, height, attributes.pads);
default:
throw new Error('Invalid mode');
}
};
const getPadConstant = (glsl, shape, strides, width, height, pads, value) => {
const rank = shape.length;
let block = '';
for (let i = rank - 1; i >= 0; --i) {
block += `
k = m[${i}] - ${pads[i]};
if (k < 0) return constant;
if (k >= ${shape[i]}) return constant;
offset += k * ${strides[i]};
`;
}
return `
float padA(int m[${rank}]) {
const float constant = float(${value});
int offset = 0;
int k = 0;
${block}
vec2 coords = offsetToCoords(offset, ${width}, ${height});
float value = getColorAsFloat(${glsl.texture2D}(A, coords));
return value;
}
`;
};
const getPadReflect = (glsl, shape, strides, width, height, pads) => {
const rank = shape.length;
let block = '';
for (let i = rank - 1; i >= 0; --i) {
block += `
k = m[${i}] - ${pads[i]};
if (k < 0) { k = -k; }
{
const int _2n_1 = ${2 * (shape[i] - 1)};
k = int( mod( float(k), float(_2n_1) ) ) ;
if(k >= ${shape[i]}) { k = _2n_1 - k; }
}
offset += k * ${strides[i]};
`;
}
return `
float padA(int m[${rank}]) {
int offset = 0;
int k = 0;
${block}
vec2 coords = offsetToCoords(offset, ${width}, ${height});
float value = getColorAsFloat(${glsl.texture2D}(A, coords));
return value;
}
`;
};
const getPadEdge = (glsl, shape, strides, width, height, pads) => {
const rank = shape.length;
let block = '';
for (let i = rank - 1; i >= 0; --i) {
block += `
k = m[${i}] - ${pads[i]};
if (k < 0) k = 0;
if (k >= ${shape[i]}) k = ${shape[i] - 1};
offset += k * ${strides[i]};
`;
}
return `
float padA(int m[${rank}]) {
int offset = 0;
int k = 0;
${block}
vec2 coords = offsetToCoords(offset, ${width}, ${height});
float value = getColorAsFloat(${glsl.texture2D}(A, coords));
return value;
}
`;
};
//# sourceMappingURL=pad.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,205 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import {AttributeWithCacheKey, createAttributeWithCacheKey} from '../../../attribute-with-cache-key';
import {Graph} from '../../../graph';
import {OperatorImplementation, OperatorInitialization} from '../../../operators';
import {Tensor} from '../../../tensor';
import {ShapeUtil} from '../../../util';
import {getGlsl, Glsl} from '../glsl-source';
import {WebGLInferenceHandler} from '../inference-handler';
import {ProgramInfo, TextureType} from '../types';
export interface PadAttributes extends AttributeWithCacheKey {
readonly mode: string;
readonly pads: number[];
readonly value: number;
}
const padProgramMetadata = {
name: 'Pad',
inputNames: ['A'],
inputTypes: [TextureType.unpacked],
};
export const padV2: OperatorImplementation<PadAttributes> =
(inferenceHandler: WebGLInferenceHandler, inputs: Tensor[], attributes: PadAttributes): Tensor[] => {
validateInputsV2(inputs);
const output = inferenceHandler.run(
{
...padProgramMetadata,
cacheHint: attributes.cacheKey,
get: () => createPadProgramInfo(inferenceHandler, inputs[0], attributes)
},
inputs);
return [output];
};
export const parsePadAttributesV2: OperatorInitialization<PadAttributes> = (node: Graph.Node): PadAttributes => {
const mode = node.attributes.getString('mode', 'constant');
const value = node.attributes.getFloat('value', 0.0);
const pads = node.attributes.getInts('pads');
return createAttributeWithCacheKey({mode, value, pads});
};
export const padV11: OperatorImplementation<string> =
(inferenceHandler: WebGLInferenceHandler, inputs: Tensor[], mode: string): Tensor[] => {
validateInputsV11(inputs);
const attrubutes = generatePadAttributesFromInputs(inferenceHandler, inputs, mode);
return padV2(inferenceHandler, [inputs[0]], attrubutes);
};
export const parsePadAttributesV11: OperatorInitialization<string> = (node: Graph.Node): string =>
node.attributes.getString('mode', 'constant');
const generatePadAttributesFromInputs =
(inferenceHandler: WebGLInferenceHandler, inputs: Tensor[], mode: string): PadAttributes => {
if (!inferenceHandler.session.isInitializer(inputs[1].dataId) ||
(inputs.length >= 3 && !inferenceHandler.session.isInitializer(inputs[2].dataId))) {
throw new Error('dynamic pad attributes are not allowed');
}
const pads = Array.from(inputs[1].integerData);
const value = (inputs.length >= 3) ? inputs[2].floatData[0] : 0.0;
return createAttributeWithCacheKey({mode, pads, value});
};
const createPadProgramInfo =
(inferenceHandler: WebGLInferenceHandler, input: Tensor, attributes: PadAttributes): ProgramInfo => {
const outputShape = ShapeUtil.padShape(input.dims.slice(), attributes.pads);
const rank = outputShape.length;
const padFunction = getPadFunction(inferenceHandler, input, attributes);
const shaderSource = `
${padFunction}
float process(int[${rank}] indices) {
return padA(indices);
}`;
return {
name: 'Pad',
inputNames: ['A'],
inputTypes: [TextureType.unpacked],
output: {dims: outputShape, type: input.type, textureType: TextureType.unpacked},
shaderSource
};
};
const validateInputsV2 = (inputs: Tensor[]): void => {
if (!inputs || inputs.length !== 1) {
throw new Error('Pad requires 1 input');
}
if (inputs[0].type !== 'float32' && inputs[0].type !== 'float64') {
throw new Error('Invalid input type.');
}
};
const validateInputsV11 = (inputs: Tensor[]): void => {
if (!inputs || (inputs.length !== 2 && inputs.length !== 3)) {
throw new Error('Pad requires 2 or 3 inputs');
}
if (inputs[1].type !== 'int32') {
throw new Error('Invalid input type.');
}
if (inputs.length >= 3 && inputs[2].type === 'string') {
throw new Error('Invalid input type.');
}
};
const getPadFunction = (inferenceHandler: WebGLInferenceHandler, input: Tensor, attributes: PadAttributes): string => {
const glsl = getGlsl(inferenceHandler.session.backend.glContext.version);
const [width, height] = inferenceHandler.calculateTextureWidthAndHeight(input.dims, TextureType.unpacked);
const strides = ShapeUtil.computeStrides(input.dims);
switch (attributes.mode) {
case 'constant':
return getPadConstant(glsl, input.dims, strides, width, height, attributes.pads, attributes.value);
case 'reflect':
return getPadReflect(glsl, input.dims, strides, width, height, attributes.pads);
case 'edge':
return getPadEdge(glsl, input.dims, strides, width, height, attributes.pads);
default:
throw new Error('Invalid mode');
}
};
const getPadConstant =
(glsl: Glsl, shape: readonly number[], strides: readonly number[], width: number, height: number, pads: number[],
value: number): string => {
const rank = shape.length;
let block = '';
for (let i = rank - 1; i >= 0; --i) {
block += `
k = m[${i}] - ${pads[i]};
if (k < 0) return constant;
if (k >= ${shape[i]}) return constant;
offset += k * ${strides[i]};
`;
}
return `
float padA(int m[${rank}]) {
const float constant = float(${value});
int offset = 0;
int k = 0;
${block}
vec2 coords = offsetToCoords(offset, ${width}, ${height});
float value = getColorAsFloat(${glsl.texture2D}(A, coords));
return value;
}
`;
};
const getPadReflect =
(glsl: Glsl, shape: readonly number[], strides: readonly number[], width: number, height: number, pads: number[]):
string => {
const rank = shape.length;
let block = '';
for (let i = rank - 1; i >= 0; --i) {
block += `
k = m[${i}] - ${pads[i]};
if (k < 0) { k = -k; }
{
const int _2n_1 = ${2 * (shape[i] - 1)};
k = int( mod( float(k), float(_2n_1) ) ) ;
if(k >= ${shape[i]}) { k = _2n_1 - k; }
}
offset += k * ${strides[i]};
`;
}
return `
float padA(int m[${rank}]) {
int offset = 0;
int k = 0;
${block}
vec2 coords = offsetToCoords(offset, ${width}, ${height});
float value = getColorAsFloat(${glsl.texture2D}(A, coords));
return value;
}
`;
};
const getPadEdge =
(glsl: Glsl, shape: readonly number[], strides: readonly number[], width: number, height: number, pads: number[]):
string => {
const rank = shape.length;
let block = '';
for (let i = rank - 1; i >= 0; --i) {
block += `
k = m[${i}] - ${pads[i]};
if (k < 0) k = 0;
if (k >= ${shape[i]}) k = ${shape[i] - 1};
offset += k * ${strides[i]};
`;
}
return `
float padA(int m[${rank}]) {
int offset = 0;
int k = 0;
${block}
vec2 coords = offsetToCoords(offset, ${width}, ${height});
float value = getColorAsFloat(${glsl.texture2D}(A, coords));
return value;
}
`;
};

View File

@@ -0,0 +1,304 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.globalMaxPool = exports.parseMaxPoolAttributes = exports.maxPool = exports.parseGlobalAveragePoolAttributes = exports.globalAveragePool = exports.parseAveragePoolAttributes = exports.averagePool = void 0;
const attribute_with_cache_key_1 = require("../../../attribute-with-cache-key");
const util_1 = require("../../../util");
const types_1 = require("../types");
const averagePool = (inferenceHandler, inputs, attributes) => {
validateInputs(inputs);
const metadata = { name: 'AveragePool', inputNames: ['X'], inputTypes: [types_1.TextureType.unpacked], cacheHint: attributes.cacheKey };
const output = inferenceHandler.run(Object.assign(Object.assign({}, metadata), { get: () => createAveragePoolProgramInfo(inputs, metadata, false, attributes) }), inputs);
return [output];
};
exports.averagePool = averagePool;
const parseAveragePoolAttributes = (node) => {
const autoPad = node.attributes.getString('auto_pad', 'NOTSET');
const ceilMode = node.attributes.getInt('ceil_mode', 0);
const countIncludePad = (node.attributes.getInt('count_include_pad', 0) === 0 ? false : true);
const kernelShape = node.attributes.getInts('kernel_shape');
const strides = node.attributes.getInts('strides', []);
const pads = node.attributes.getInts('pads', []);
// TODO: support attribute 'ceil_mode'
if (ceilMode !== 0) {
throw new Error('using ceil() in shape computation is not yet supported for AveragePool');
}
return (0, attribute_with_cache_key_1.createAttributeWithCacheKey)({ autoPad, ceilMode, countIncludePad, kernelShape, strides, pads });
};
exports.parseAveragePoolAttributes = parseAveragePoolAttributes;
const createAveragePoolProgramInfo = (inputs, metadata, isGlobalOperator, attributes) => {
const [adjustedAttributes, outputShape] = getAdjustedPoolAttributesAndOutputShape(inputs, attributes, isGlobalOperator);
const kernelSize = util_1.ShapeUtil.size(adjustedAttributes.kernelShape);
const op1 = 'value += _X(x);';
let op2 = '';
if (adjustedAttributes.countIncludePad) {
op2 += `value /= float(${kernelSize});`;
}
else {
op2 += `value /= float(${kernelSize} - pad);`;
}
const poolingCode = generatePoolingCode(inputs[0].dims, adjustedAttributes, op1, op2, '0.0');
const shaderSource = `
${poolingCode}
`;
return Object.assign(Object.assign({}, metadata), { output: { dims: outputShape, type: inputs[0].type, textureType: types_1.TextureType.unpacked }, shaderSource });
};
const globalAveragePool = (inferenceHandler, inputs, attributes) => {
validateInputs(inputs);
const metadata = {
name: 'GlobalAveragePool',
inputNames: ['X'],
inputTypes: [types_1.TextureType.unpacked],
cacheHint: `${attributes.countIncludePad}`
};
const output = inferenceHandler.run(Object.assign(Object.assign({}, metadata), { get: () => createAveragePoolProgramInfo(inputs, metadata, true, attributes) }), inputs);
return [output];
};
exports.globalAveragePool = globalAveragePool;
const parseGlobalAveragePoolAttributes = (node) => {
const countIncludePad = (node.attributes.getInt('count_include_pad', 0) === 0 ? false : true);
return (0, attribute_with_cache_key_1.createAttributeWithCacheKey)({ autoPad: '', ceilMode: 0, countIncludePad, kernelShape: [], strides: [], pads: [] });
};
exports.parseGlobalAveragePoolAttributes = parseGlobalAveragePoolAttributes;
const maxPool = (inferenceHandler, inputs, attributes) => {
validateInputs(inputs);
const metadata = { name: 'MaxPool', inputNames: ['X'], inputTypes: [types_1.TextureType.unpacked], cacheHint: attributes.cacheKey };
const output = inferenceHandler.run(Object.assign(Object.assign({}, metadata), { get: () => createMaxPoolProgramInfo(inputs, metadata, false, attributes) }), inputs);
return [output];
};
exports.maxPool = maxPool;
const parseMaxPoolAttributes = (node) => {
const autoPad = node.attributes.getString('auto_pad', 'NOTSET');
const ceilMode = node.attributes.getInt('ceil_mode', 0);
const kernelShape = node.attributes.getInts('kernel_shape');
const strides = node.attributes.getInts('strides', []);
const pads = node.attributes.getInts('pads', []);
const storageOrder = node.attributes.getInt('storage_order', 0);
const dilations = node.attributes.getInts('dilations', []);
// TODO: support attribute 'ceil_mode' and 'storage_order'
if (storageOrder !== 0) {
throw new Error('column major storage order is not yet supported for MaxPool');
}
if (ceilMode !== 0) {
throw new Error('using ceil() in shape computation is not yet supported for MaxPool');
}
return (0, attribute_with_cache_key_1.createAttributeWithCacheKey)({ autoPad, ceilMode, countIncludePad: false, kernelShape, strides, pads, storageOrder, dilations });
};
exports.parseMaxPoolAttributes = parseMaxPoolAttributes;
const createMaxPoolProgramInfo = (inputs, metadata, isGlobalOperator, attributes) => {
const [adjustedAttributes, outputShape] = getAdjustedPoolAttributesAndOutputShape(inputs, attributes, isGlobalOperator);
const op1 = `
value = max(_X(x), value);
`;
const op2 = '';
const poolingCode = generatePoolingCode(inputs[0].dims, adjustedAttributes, op1, op2, '-1e5');
const shaderSource = `
${poolingCode}
`;
return Object.assign(Object.assign({}, metadata), { output: { dims: outputShape, type: inputs[0].type, textureType: types_1.TextureType.unpacked }, shaderSource });
};
const getAdjustedPoolAttributesAndOutputShape = (inputs, attributes, isGlobalOperator) => {
const inputShape = inputs[0].dims.slice();
const hasDilations = Object.hasOwnProperty.call(attributes, 'dilations');
const kernelShape = attributes.kernelShape.slice();
const strides = attributes.strides.slice();
const dilations = hasDilations ? attributes.dilations.slice() : [];
const pads = attributes.pads.slice();
util_1.PoolConvUtil.adjustPoolAttributes(isGlobalOperator, inputShape, kernelShape, strides, dilations, pads);
const outputShape = util_1.PoolConvUtil.computePoolOutputShape(isGlobalOperator, inputShape, strides, dilations, kernelShape, pads, attributes.autoPad);
const newAttributes = Object.assign({}, attributes);
if (hasDilations) {
Object.assign(newAttributes, { kernelShape, strides, pads, dilations, cacheKey: attributes.cacheKey });
}
else {
Object.assign(newAttributes, { kernelShape, strides, pads, cacheKey: attributes.cacheKey });
}
return [newAttributes, outputShape];
};
const globalMaxPoolAttributes = {
autoPad: '',
ceilMode: 0,
countIncludePad: false,
kernelShape: [],
strides: [],
pads: [],
storageOrder: 0,
dilations: [],
cacheKey: ''
};
const globalMaxPoolMetadata = {
name: 'GlobalMaxPool',
inputNames: ['X'],
inputTypes: [types_1.TextureType.unpacked]
};
const globalMaxPool = (inferenceHandler, inputs) => {
validateInputs(inputs);
const output = inferenceHandler.run(Object.assign(Object.assign({}, globalMaxPoolMetadata), { get: () => createMaxPoolProgramInfo(inputs, globalMaxPoolMetadata, true, globalMaxPoolAttributes) }), inputs);
return [output];
};
exports.globalMaxPool = globalMaxPool;
const validateInputs = (inputs) => {
if (!inputs || inputs.length !== 1) {
throw new Error('Pool ops requires 1 input.');
}
if (inputs[0].type !== 'float32' && inputs[0].type !== 'float64') {
throw new Error('Invalid input type.');
}
};
const generatePoolingCode = (inputDims, attributes, op1, op2, start) => {
const rank = inputDims.length;
if (attributes.kernelShape.length <= 2) {
const kw = attributes.kernelShape[attributes.kernelShape.length - 1];
const sw = attributes.strides[attributes.strides.length - 1];
const pwStart = attributes.pads[attributes.pads.length / 2 - 1];
const pwEnd = attributes.pads[attributes.pads.length - 1];
const dimW = inputDims[rank - 1];
let codeW = '';
let codeH = '';
let codeHEnd = '';
if (pwStart + pwEnd !== 0) {
codeW = `
for (int i = 0; i < ${kw}; i++) {
x[${rank} - 1] = indices[${rank} - 1] * ${sw} - ${pwStart} + i;
if (x[${rank} - 1] < 0 || x[${rank} - 1] >= ${dimW}) {
pad++;
continue;
}
${op1}
}`;
}
else {
codeW = `
for (int i = 0; i < ${kw}; i++) {
x[${rank} - 1] = indices[${rank} - 1] * ${sw} - ${pwStart} + i;
${op1}
}`;
}
if (attributes.kernelShape.length === 2) {
const kh = attributes.kernelShape[attributes.kernelShape.length - 2];
const sh = attributes.strides[attributes.strides.length - 2];
const phStart = attributes.pads[attributes.pads.length / 2 - 2];
const phEnd = attributes.pads[attributes.pads.length - 2];
const dimH = inputDims[rank - 2];
if (phStart + phEnd !== 0) {
codeH = `
for (int j = 0; j < ${kh}; j++) {
x[${rank} - 2] = indices[${rank} - 2] * ${sh} - ${phStart} + j;
if (x[${rank} - 2] < 0 || x[${rank} - 2] >= ${dimH}) {
pad+= ${kw};
continue;
}
`;
}
else {
codeH = `
for (int j = 0; j < ${kh}; j++) {
x[${rank} - 2] = indices[${rank} - 2] * ${sh} - ${phStart} + j;
`;
}
codeHEnd = `
}
`;
}
const poolingCode = `
float process(int indices[${rank}]) {
int x[${rank}];
copyVec(indices, x);
float value = ${start};
int pad = 0;
${codeH}
${codeW}
${codeHEnd}
${op2}
return value;
}
`;
return poolingCode;
}
else {
const kernelSize = util_1.ShapeUtil.size(attributes.kernelShape);
const kernelStrides = util_1.ShapeUtil.computeStrides(attributes.kernelShape);
const stridesRank = kernelStrides.length;
const padsRank = attributes.pads.length;
const offsetToIndicesFunction = offsetToIndices(stridesRank);
const copyInputDims = copyArray(inputDims, 'inputDims');
const copyPads = copyArray(attributes.pads, 'pads');
const copyKernelStrides = copyArray(kernelStrides, 'kernelStrides');
const copyStrides = copyArray(attributes.strides, 'strides');
const hasPads = attributes.pads.reduce((sum, cur) => sum + cur);
let padCode = '';
if (hasPads) {
padCode = `
if (x[j] >= inputDims[j] || x[j] < 0) {
pad++;
isPad = true;
break;
}
}
if (!isPad) {
${op1}
}`;
}
else {
padCode = `
}
${op1}
`;
}
const poolingCode = `
${offsetToIndicesFunction}
float process(int indices[${rank}]) {
int x[${rank}];
copyVec(indices, x);
int offset[${stridesRank}];
int pads[${padsRank}];
int inputDims[${rank}];
int kernelStrides[${stridesRank}];
int strides[${stridesRank}];
${copyPads}
${copyInputDims}
${copyStrides}
${copyKernelStrides}
float value = ${start};
int pad = 0;
bool isPad = false;
for (int i = 0; i < ${kernelSize}; i++) {
offsetToIndices(i, kernelStrides, offset);
isPad = false;
for (int j = ${rank} - ${stridesRank}; j < ${rank}; j++) {
x[j] = indices[j] * strides[j - ${rank} + ${stridesRank}]
+ offset[j - ${rank} + ${stridesRank}] - pads[j - 2];
${padCode}
}
${op2}
return value;
}
`;
return poolingCode;
}
};
const copyArray = (array, arrayName) => {
let block = '';
for (let i = 0; i < array.length; i++) {
block += `
${arrayName}[${i}] = ${array[i]};
`;
}
return block;
};
const offsetToIndices = (rank) => `
void offsetToIndices(int offset, int[${rank}] strides, out int[${rank}] indices) {
if (${rank} == 0) {
return;
}
for (int i = 0; i < ${rank} - 1; ++i) {
indices[i] = offset / strides[i];
offset -= indices[i] * strides[i];
}
indices[${rank} - 1] = offset;
}`;
//# sourceMappingURL=pool.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,367 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import {AttributeWithCacheKey, createAttributeWithCacheKey} from '../../../attribute-with-cache-key';
import {Graph} from '../../../graph';
import {OperatorImplementation, OperatorInitialization} from '../../../operators';
import {Tensor} from '../../../tensor';
import {PoolConvUtil, ShapeUtil} from '../../../util';
import {WebGLInferenceHandler} from '../inference-handler';
import {ProgramInfo, ProgramMetadata, TextureType} from '../types';
export interface AveragePoolAttributes extends AttributeWithCacheKey {
readonly autoPad: string;
readonly ceilMode: number;
readonly countIncludePad: boolean;
readonly kernelShape: readonly number[];
readonly strides: readonly number[];
readonly pads: readonly number[];
}
export const averagePool: OperatorImplementation<AveragePoolAttributes> =
(inferenceHandler: WebGLInferenceHandler, inputs: Tensor[], attributes: AveragePoolAttributes): Tensor[] => {
validateInputs(inputs);
const metadata =
{name: 'AveragePool', inputNames: ['X'], inputTypes: [TextureType.unpacked], cacheHint: attributes.cacheKey};
const output = inferenceHandler.run(
{...metadata, get: () => createAveragePoolProgramInfo(inputs, metadata, false, attributes)}, inputs);
return [output];
};
export const parseAveragePoolAttributes: OperatorInitialization<AveragePoolAttributes> =
(node: Graph.Node): AveragePoolAttributes => {
const autoPad = node.attributes.getString('auto_pad', 'NOTSET');
const ceilMode = node.attributes.getInt('ceil_mode', 0);
const countIncludePad = (node.attributes.getInt('count_include_pad', 0) === 0 ? false : true);
const kernelShape = node.attributes.getInts('kernel_shape');
const strides = node.attributes.getInts('strides', []);
const pads = node.attributes.getInts('pads', []);
// TODO: support attribute 'ceil_mode'
if (ceilMode !== 0) {
throw new Error('using ceil() in shape computation is not yet supported for AveragePool');
}
return createAttributeWithCacheKey({autoPad, ceilMode, countIncludePad, kernelShape, strides, pads});
};
const createAveragePoolProgramInfo =
(inputs: Tensor[], metadata: ProgramMetadata, isGlobalOperator: boolean, attributes: AveragePoolAttributes):
ProgramInfo => {
const [adjustedAttributes, outputShape] =
getAdjustedPoolAttributesAndOutputShape(inputs, attributes, isGlobalOperator);
const kernelSize = ShapeUtil.size(adjustedAttributes.kernelShape);
const op1 = 'value += _X(x);';
let op2 = '';
if (adjustedAttributes.countIncludePad) {
op2 += `value /= float(${kernelSize});`;
} else {
op2 += `value /= float(${kernelSize} - pad);`;
}
const poolingCode = generatePoolingCode(inputs[0].dims, adjustedAttributes, op1, op2, '0.0');
const shaderSource = `
${poolingCode}
`;
return {
...metadata,
output: {dims: outputShape, type: inputs[0].type, textureType: TextureType.unpacked},
shaderSource
};
};
export const globalAveragePool: OperatorImplementation<AveragePoolAttributes> =
(inferenceHandler: WebGLInferenceHandler, inputs: Tensor[], attributes: AveragePoolAttributes): Tensor[] => {
validateInputs(inputs);
const metadata = {
name: 'GlobalAveragePool',
inputNames: ['X'],
inputTypes: [TextureType.unpacked],
cacheHint: `${attributes.countIncludePad}`
};
const output = inferenceHandler.run(
{...metadata, get: () => createAveragePoolProgramInfo(inputs, metadata, true, attributes)}, inputs);
return [output];
};
export const parseGlobalAveragePoolAttributes: OperatorInitialization<AveragePoolAttributes> =
(node: Graph.Node): AveragePoolAttributes => {
const countIncludePad = (node.attributes.getInt('count_include_pad', 0) === 0 ? false : true);
return createAttributeWithCacheKey(
{autoPad: '', ceilMode: 0, countIncludePad, kernelShape: [], strides: [], pads: []});
};
export interface MaxPoolAttributes extends AveragePoolAttributes {
readonly storageOrder: number;
readonly dilations: number[];
}
export const maxPool: OperatorImplementation<MaxPoolAttributes> =
(inferenceHandler: WebGLInferenceHandler, inputs: Tensor[], attributes: MaxPoolAttributes): Tensor[] => {
validateInputs(inputs);
const metadata =
{name: 'MaxPool', inputNames: ['X'], inputTypes: [TextureType.unpacked], cacheHint: attributes.cacheKey};
const output = inferenceHandler.run(
{...metadata, get: () => createMaxPoolProgramInfo(inputs, metadata, false, attributes)}, inputs);
return [output];
};
export const parseMaxPoolAttributes: OperatorInitialization<MaxPoolAttributes> =
(node: Graph.Node): MaxPoolAttributes => {
const autoPad = node.attributes.getString('auto_pad', 'NOTSET');
const ceilMode = node.attributes.getInt('ceil_mode', 0);
const kernelShape = node.attributes.getInts('kernel_shape');
const strides = node.attributes.getInts('strides', []);
const pads = node.attributes.getInts('pads', []);
const storageOrder = node.attributes.getInt('storage_order', 0);
const dilations = node.attributes.getInts('dilations', []);
// TODO: support attribute 'ceil_mode' and 'storage_order'
if (storageOrder !== 0) {
throw new Error('column major storage order is not yet supported for MaxPool');
}
if (ceilMode !== 0) {
throw new Error('using ceil() in shape computation is not yet supported for MaxPool');
}
return createAttributeWithCacheKey(
{autoPad, ceilMode, countIncludePad: false, kernelShape, strides, pads, storageOrder, dilations});
};
const createMaxPoolProgramInfo =
(inputs: Tensor[], metadata: ProgramMetadata, isGlobalOperator: boolean, attributes: MaxPoolAttributes):
ProgramInfo => {
const [adjustedAttributes, outputShape] =
getAdjustedPoolAttributesAndOutputShape(inputs, attributes, isGlobalOperator);
const op1 = `
value = max(_X(x), value);
`;
const op2 = '';
const poolingCode = generatePoolingCode(inputs[0].dims, adjustedAttributes, op1, op2, '-1e5');
const shaderSource = `
${poolingCode}
`;
return {
...metadata,
output: {dims: outputShape, type: inputs[0].type, textureType: TextureType.unpacked},
shaderSource
};
};
const getAdjustedPoolAttributesAndOutputShape =
(inputs: Tensor[], attributes: AveragePoolAttributes|MaxPoolAttributes, isGlobalOperator: boolean):
[AveragePoolAttributes|MaxPoolAttributes, number[]] => {
const inputShape = inputs[0].dims.slice();
const hasDilations = Object.hasOwnProperty.call(attributes, 'dilations');
const kernelShape = attributes.kernelShape.slice();
const strides = attributes.strides.slice();
const dilations: number[] = hasDilations ? (attributes as MaxPoolAttributes).dilations.slice() : [];
const pads = attributes.pads.slice();
PoolConvUtil.adjustPoolAttributes(isGlobalOperator, inputShape, kernelShape, strides, dilations, pads);
const outputShape = PoolConvUtil.computePoolOutputShape(
isGlobalOperator, inputShape, strides, dilations, kernelShape, pads, attributes.autoPad);
const newAttributes = Object.assign({}, attributes);
if (hasDilations) {
Object.assign(newAttributes, {kernelShape, strides, pads, dilations, cacheKey: attributes.cacheKey});
} else {
Object.assign(newAttributes, {kernelShape, strides, pads, cacheKey: attributes.cacheKey});
}
return [newAttributes, outputShape];
};
const globalMaxPoolAttributes = {
autoPad: '',
ceilMode: 0,
countIncludePad: false,
kernelShape: [],
strides: [],
pads: [],
storageOrder: 0,
dilations: [],
cacheKey: ''
};
const globalMaxPoolMetadata = {
name: 'GlobalMaxPool',
inputNames: ['X'],
inputTypes: [TextureType.unpacked]
};
export const globalMaxPool = (inferenceHandler: WebGLInferenceHandler, inputs: Tensor[]): Tensor[] => {
validateInputs(inputs);
const output = inferenceHandler.run(
{
...globalMaxPoolMetadata,
get: () => createMaxPoolProgramInfo(inputs, globalMaxPoolMetadata, true, globalMaxPoolAttributes)
},
inputs);
return [output];
};
const validateInputs = (inputs: Tensor[]): void => {
if (!inputs || inputs.length !== 1) {
throw new Error('Pool ops requires 1 input.');
}
if (inputs[0].type !== 'float32' && inputs[0].type !== 'float64') {
throw new Error('Invalid input type.');
}
};
const generatePoolingCode =
(inputDims: readonly number[], attributes: AveragePoolAttributes, op1: string, op2: string, start: string):
string => {
const rank = inputDims.length;
if (attributes.kernelShape.length <= 2) {
const kw = attributes.kernelShape[attributes.kernelShape.length - 1];
const sw = attributes.strides[attributes.strides.length - 1];
const pwStart = attributes.pads[attributes.pads.length / 2 - 1];
const pwEnd = attributes.pads[attributes.pads.length - 1];
const dimW = inputDims[rank - 1];
let codeW = '';
let codeH = '';
let codeHEnd = '';
if (pwStart + pwEnd !== 0) {
codeW = `
for (int i = 0; i < ${kw}; i++) {
x[${rank} - 1] = indices[${rank} - 1] * ${sw} - ${pwStart} + i;
if (x[${rank} - 1] < 0 || x[${rank} - 1] >= ${dimW}) {
pad++;
continue;
}
${op1}
}`;
} else {
codeW = `
for (int i = 0; i < ${kw}; i++) {
x[${rank} - 1] = indices[${rank} - 1] * ${sw} - ${pwStart} + i;
${op1}
}`;
}
if (attributes.kernelShape.length === 2) {
const kh = attributes.kernelShape[attributes.kernelShape.length - 2];
const sh = attributes.strides[attributes.strides.length - 2];
const phStart = attributes.pads[attributes.pads.length / 2 - 2];
const phEnd = attributes.pads[attributes.pads.length - 2];
const dimH = inputDims[rank - 2];
if (phStart + phEnd !== 0) {
codeH = `
for (int j = 0; j < ${kh}; j++) {
x[${rank} - 2] = indices[${rank} - 2] * ${sh} - ${phStart} + j;
if (x[${rank} - 2] < 0 || x[${rank} - 2] >= ${dimH}) {
pad+= ${kw};
continue;
}
`;
} else {
codeH = `
for (int j = 0; j < ${kh}; j++) {
x[${rank} - 2] = indices[${rank} - 2] * ${sh} - ${phStart} + j;
`;
}
codeHEnd = `
}
`;
}
const poolingCode = `
float process(int indices[${rank}]) {
int x[${rank}];
copyVec(indices, x);
float value = ${start};
int pad = 0;
${codeH}
${codeW}
${codeHEnd}
${op2}
return value;
}
`;
return poolingCode;
} else {
const kernelSize = ShapeUtil.size(attributes.kernelShape);
const kernelStrides = ShapeUtil.computeStrides(attributes.kernelShape);
const stridesRank = kernelStrides.length;
const padsRank = attributes.pads.length;
const offsetToIndicesFunction = offsetToIndices(stridesRank);
const copyInputDims = copyArray(inputDims, 'inputDims');
const copyPads = copyArray(attributes.pads, 'pads');
const copyKernelStrides = copyArray(kernelStrides, 'kernelStrides');
const copyStrides = copyArray(attributes.strides, 'strides');
const hasPads = attributes.pads.reduce((sum, cur) => sum + cur);
let padCode = '';
if (hasPads) {
padCode = `
if (x[j] >= inputDims[j] || x[j] < 0) {
pad++;
isPad = true;
break;
}
}
if (!isPad) {
${op1}
}`;
} else {
padCode = `
}
${op1}
`;
}
const poolingCode = `
${offsetToIndicesFunction}
float process(int indices[${rank}]) {
int x[${rank}];
copyVec(indices, x);
int offset[${stridesRank}];
int pads[${padsRank}];
int inputDims[${rank}];
int kernelStrides[${stridesRank}];
int strides[${stridesRank}];
${copyPads}
${copyInputDims}
${copyStrides}
${copyKernelStrides}
float value = ${start};
int pad = 0;
bool isPad = false;
for (int i = 0; i < ${kernelSize}; i++) {
offsetToIndices(i, kernelStrides, offset);
isPad = false;
for (int j = ${rank} - ${stridesRank}; j < ${rank}; j++) {
x[j] = indices[j] * strides[j - ${rank} + ${stridesRank}]
+ offset[j - ${rank} + ${stridesRank}] - pads[j - 2];
${padCode}
}
${op2}
return value;
}
`;
return poolingCode;
}
};
const copyArray = (array: readonly number[], arrayName: string): string => {
let block = '';
for (let i = 0; i < array.length; i++) {
block += `
${arrayName}[${i}] = ${array[i]};
`;
}
return block;
};
const offsetToIndices = (rank: number): string => `
void offsetToIndices(int offset, int[${rank}] strides, out int[${rank}] indices) {
if (${rank} == 0) {
return;
}
for (int i = 0; i < ${rank} - 1; ++i) {
indices[i] = offset / strides[i];
offset -= indices[i] * strides[i];
}
indices[${rank} - 1] = offset;
}`;

View File

@@ -0,0 +1,131 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.reduceLogSumSquare = exports.reduceLogSum = exports.reduceProd = exports.reduceMin = exports.reduceMax = exports.reduceMean = exports.reduceSum = exports.parseReduceAttributes = void 0;
const attribute_with_cache_key_1 = require("../../../attribute-with-cache-key");
const operators_1 = require("../../../operators");
const util_1 = require("../../../util");
const types_1 = require("../types");
const reduce = (inferenceHandler, inputs, attributes, name, reduceOp) => {
validateInputs(inputs);
const reduceProgramMetadata = {
name,
inputNames: ['A'],
inputTypes: [types_1.TextureType.unpacked],
};
const output = inferenceHandler.run(Object.assign(Object.assign({}, reduceProgramMetadata), { cacheHint: attributes.cacheKey, get: () => createReduceProgramInfo(inferenceHandler, inputs, attributes, name, reduceOp, reduceProgramMetadata) }), inputs);
return [output];
};
const parseReduceAttributes = (node) => {
const axes = node.attributes.getInts('axes', []);
const keepDims = node.attributes.getInt('keepdims', 1) === 1;
return (0, attribute_with_cache_key_1.createAttributeWithCacheKey)({ axes, keepDims });
};
exports.parseReduceAttributes = parseReduceAttributes;
const createReduceProgramInfo = (handler, inputs, attributes, name, reduceOp, reduceProgramMetadata) => {
const outputShape = [];
const iRank = inputs[0].dims.length || 1;
const idxCopy = []; // copy output indexes to input indexes
const axes = util_1.ShapeUtil.normalizeAxes(attributes.axes, inputs[0].dims.length);
const ops = reduceOp(inputs, axes);
let reduceOps = ops[1];
for (let k = 0; k < inputs[0].dims.length; k++) {
// if this axis is reduced
if (axes.indexOf(k) >= 0 || axes.length === 0) {
if (attributes.keepDims) {
outputShape.push(1);
} // else { remove the axis from outputShape; }
// loop over the d-th axis
reduceOps = `
for(int j${k} = 0; j${k} < ${inputs[0].dims[k]}; j${k}++) {
inputIdx[${k}] = j${k};
${reduceOps}
}`;
}
else {
idxCopy.push(`inputIdx[${k}] = outputIdx[${outputShape.length}];`);
outputShape.push(inputs[0].dims[k]);
}
}
const oRank = outputShape.length || 1;
const shaderSource = `
float process(int outputIdx[${oRank}]) {
float value; // final result
int inputIdx[${iRank}]; // addressing input data
${idxCopy.join('\n')}
${ops[0]} // init ops for reduce max/min
${reduceOps}
${ops[2]} // final computation for reduce mean
return value;
}`;
return Object.assign(Object.assign({}, reduceProgramMetadata), { output: { dims: outputShape, type: inputs[0].type, textureType: types_1.TextureType.unpacked }, shaderSource });
};
const validateInputs = (inputs) => {
if (!inputs || inputs.length !== 1) {
throw new Error('Reduce op requires 1 input.');
}
if (operators_1.NUMBER_TYPES.indexOf(inputs[0].type) === -1) {
throw new Error('Invalid input type.');
}
};
const reduceSum = (inferenceHandler, inputs, attributes) => {
const reduceOp = () => ['value = 0.0;', 'value += _A(inputIdx);', ''];
return reduce(inferenceHandler, inputs, attributes, 'ReduceSum', reduceOp);
};
exports.reduceSum = reduceSum;
const reduceMean = (inferenceHandler, inputs, attributes) => {
const reduceOp = (inputs, axes) => {
let size = 1.0;
for (let k = 0; k < inputs[0].dims.length; k++) {
if (axes.indexOf(k) >= 0 || axes.length === 0) {
size *= inputs[0].dims[k];
}
}
return ['value = 0.0;', 'value += _A(inputIdx);', `value /= ${size}.;`]; // ensure real number with `.`
};
return reduce(inferenceHandler, inputs, attributes, 'ReduceMean', reduceOp);
};
exports.reduceMean = reduceMean;
const reduceMax = (inferenceHandler, inputs, attributes) => {
const reduceOp = (inputs, axes) => {
const idxZero = [];
for (let k = 0; k < inputs[0].dims.length; k++) {
if (axes.indexOf(k) >= 0 || axes.length === 0) {
idxZero.push(`inputIdx[${k}] = 0;`); // first element
}
}
return [`${idxZero.join('\n')}\nvalue = _A(inputIdx);`, 'value = max(value, _A(inputIdx));', ''];
};
return reduce(inferenceHandler, inputs, attributes, 'ReduceMax', reduceOp);
};
exports.reduceMax = reduceMax;
const reduceMin = (inferenceHandler, inputs, attributes) => {
const reduceOp = (inputs, axes) => {
const idxZero = [];
for (let k = 0; k < inputs[0].dims.length; k++) {
if (axes.indexOf(k) >= 0 || axes.length === 0) {
idxZero.push(`inputIdx[${k}] = 0;`); // first element
}
}
return [`${idxZero.join('\n')}\nvalue = _A(inputIdx);`, 'value = min(value, _A(inputIdx));', ''];
};
return reduce(inferenceHandler, inputs, attributes, 'ReduceMin', reduceOp);
};
exports.reduceMin = reduceMin;
const reduceProd = (inferenceHandler, inputs, attributes) => {
const reduceOp = () => ['value = 1.0;', 'value *= _A(inputIdx);', ''];
return reduce(inferenceHandler, inputs, attributes, 'ReduceProd', reduceOp);
};
exports.reduceProd = reduceProd;
const reduceLogSum = (inferenceHandler, inputs, attributes) => {
const reduceOp = () => ['value = 0.0;', 'value += _A(inputIdx);', 'value = log(value);'];
return reduce(inferenceHandler, inputs, attributes, 'ReduceLogSum', reduceOp);
};
exports.reduceLogSum = reduceLogSum;
const reduceLogSumSquare = (inferenceHandler, inputs, attributes) => {
const reduceOp = () => ['float t; value = 0.0;', 't = _A(inputIdx); value += t * t;', ''];
return reduce(inferenceHandler, inputs, attributes, 'ReduceLogSumSquare', reduceOp);
};
exports.reduceLogSumSquare = reduceLogSumSquare;
//# sourceMappingURL=reduce.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,177 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import {AttributeWithCacheKey, createAttributeWithCacheKey} from '../../../attribute-with-cache-key';
import {Graph} from '../../../graph';
import {NUMBER_TYPES, OperatorImplementation, OperatorInitialization} from '../../../operators';
import {Tensor} from '../../../tensor';
import {ShapeUtil} from '../../../util';
import {WebGLInferenceHandler} from '../inference-handler';
import {ProgramInfo, ProgramMetadata, TextureType} from '../types';
export interface ReduceAttributes extends AttributeWithCacheKey {
readonly axes: number[];
readonly keepDims: boolean;
}
// return [init ops, reduce ops, final ops]
type ReduceOp = (inputs: Tensor[], axes: number[]) => string[];
const reduce =
(inferenceHandler: WebGLInferenceHandler, inputs: Tensor[], attributes: ReduceAttributes, name: string,
reduceOp: ReduceOp): Tensor[] => {
validateInputs(inputs);
const reduceProgramMetadata = {
name,
inputNames: ['A'],
inputTypes: [TextureType.unpacked],
};
const output = inferenceHandler.run(
{
...reduceProgramMetadata,
cacheHint: attributes.cacheKey,
get: () =>
createReduceProgramInfo(inferenceHandler, inputs, attributes, name, reduceOp, reduceProgramMetadata)
},
inputs);
return [output];
};
export const parseReduceAttributes: OperatorInitialization<ReduceAttributes> = (node: Graph.Node): ReduceAttributes => {
const axes = node.attributes.getInts('axes', []);
const keepDims = node.attributes.getInt('keepdims', 1) === 1;
return createAttributeWithCacheKey({axes, keepDims});
};
const createReduceProgramInfo =
(handler: WebGLInferenceHandler, inputs: Tensor[], attributes: ReduceAttributes, name: string, reduceOp: ReduceOp,
reduceProgramMetadata: ProgramMetadata): ProgramInfo => {
const outputShape: number[] = [];
const iRank = inputs[0].dims.length || 1;
const idxCopy = []; // copy output indexes to input indexes
const axes = ShapeUtil.normalizeAxes(attributes.axes, inputs[0].dims.length);
const ops = reduceOp(inputs, axes);
let reduceOps = ops[1];
for (let k = 0; k < inputs[0].dims.length; k++) {
// if this axis is reduced
if (axes.indexOf(k) >= 0 || axes.length === 0) {
if (attributes.keepDims) {
outputShape.push(1);
} // else { remove the axis from outputShape; }
// loop over the d-th axis
reduceOps = `
for(int j${k} = 0; j${k} < ${inputs[0].dims[k]}; j${k}++) {
inputIdx[${k}] = j${k};
${reduceOps}
}`;
} else {
idxCopy.push(`inputIdx[${k}] = outputIdx[${outputShape.length}];`);
outputShape.push(inputs[0].dims[k]);
}
}
const oRank = outputShape.length || 1;
const shaderSource = `
float process(int outputIdx[${oRank}]) {
float value; // final result
int inputIdx[${iRank}]; // addressing input data
${idxCopy.join('\n')}
${ops[0]} // init ops for reduce max/min
${reduceOps}
${ops[2]} // final computation for reduce mean
return value;
}`;
return {
...reduceProgramMetadata,
output: {dims: outputShape, type: inputs[0].type, textureType: TextureType.unpacked},
shaderSource
};
};
const validateInputs = (inputs: Tensor[]): void => {
if (!inputs || inputs.length !== 1) {
throw new Error('Reduce op requires 1 input.');
}
if (NUMBER_TYPES.indexOf(inputs[0].type) === -1) {
throw new Error('Invalid input type.');
}
};
export const reduceSum: OperatorImplementation<ReduceAttributes> =
(inferenceHandler: WebGLInferenceHandler, inputs: Tensor[], attributes: ReduceAttributes): Tensor[] => {
const reduceOp: ReduceOp = (): string[] => ['value = 0.0;', 'value += _A(inputIdx);', ''];
return reduce(inferenceHandler, inputs, attributes, 'ReduceSum', reduceOp);
};
export const reduceMean: OperatorImplementation<ReduceAttributes> =
(inferenceHandler: WebGLInferenceHandler, inputs: Tensor[], attributes: ReduceAttributes): Tensor[] => {
const reduceOp: ReduceOp = (inputs: Tensor[], axes: number[]): string[] => {
let size = 1.0;
for (let k = 0; k < inputs[0].dims.length; k++) {
if (axes.indexOf(k) >= 0 || axes.length === 0) {
size *= inputs[0].dims[k];
}
}
return ['value = 0.0;', 'value += _A(inputIdx);', `value /= ${size}.;`]; // ensure real number with `.`
};
return reduce(inferenceHandler, inputs, attributes, 'ReduceMean', reduceOp);
};
export const reduceMax: OperatorImplementation<ReduceAttributes> =
(inferenceHandler: WebGLInferenceHandler, inputs: Tensor[], attributes: ReduceAttributes): Tensor[] => {
const reduceOp: ReduceOp = (inputs: Tensor[], axes: number[]): string[] => {
const idxZero = [];
for (let k = 0; k < inputs[0].dims.length; k++) {
if (axes.indexOf(k) >= 0 || axes.length === 0) {
idxZero.push(`inputIdx[${k}] = 0;`); // first element
}
}
return [`${idxZero.join('\n')}\nvalue = _A(inputIdx);`, 'value = max(value, _A(inputIdx));', ''];
};
return reduce(inferenceHandler, inputs, attributes, 'ReduceMax', reduceOp);
};
export const reduceMin: OperatorImplementation<ReduceAttributes> =
(inferenceHandler: WebGLInferenceHandler, inputs: Tensor[], attributes: ReduceAttributes): Tensor[] => {
const reduceOp: ReduceOp = (inputs: Tensor[], axes: number[]): string[] => {
const idxZero = [];
for (let k = 0; k < inputs[0].dims.length; k++) {
if (axes.indexOf(k) >= 0 || axes.length === 0) {
idxZero.push(`inputIdx[${k}] = 0;`); // first element
}
}
return [`${idxZero.join('\n')}\nvalue = _A(inputIdx);`, 'value = min(value, _A(inputIdx));', ''];
};
return reduce(inferenceHandler, inputs, attributes, 'ReduceMin', reduceOp);
};
export const reduceProd: OperatorImplementation<ReduceAttributes> =
(inferenceHandler: WebGLInferenceHandler, inputs: Tensor[], attributes: ReduceAttributes): Tensor[] => {
const reduceOp: ReduceOp = (): string[] => ['value = 1.0;', 'value *= _A(inputIdx);', ''];
return reduce(inferenceHandler, inputs, attributes, 'ReduceProd', reduceOp);
};
export const reduceLogSum: OperatorImplementation<ReduceAttributes> =
(inferenceHandler: WebGLInferenceHandler, inputs: Tensor[], attributes: ReduceAttributes): Tensor[] => {
const reduceOp: ReduceOp = (): string[] => ['value = 0.0;', 'value += _A(inputIdx);', 'value = log(value);'];
return reduce(inferenceHandler, inputs, attributes, 'ReduceLogSum', reduceOp);
};
export const reduceLogSumSquare: OperatorImplementation<ReduceAttributes> =
(inferenceHandler: WebGLInferenceHandler, inputs: Tensor[], attributes: ReduceAttributes): Tensor[] => {
const reduceOp: ReduceOp = (): string[] => ['float t; value = 0.0;', 't = _A(inputIdx); value += t * t;', ''];
return reduce(inferenceHandler, inputs, attributes, 'ReduceLogSumSquare', reduceOp);
};

View File

@@ -0,0 +1,139 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.isReshapeCheap = exports.processDims3D = exports.createPackedReshape3DProgramInfoLoader = void 0;
const util_1 = require("../../../util");
const glsl_source_1 = require("../glsl-source");
const types_1 = require("../types");
const packing_utils_1 = require("./packing-utils");
const createPackedReshape3DProgramMetadata = (outputShape3D) => ({ name: 'Reshape (packed)', inputTypes: [types_1.TextureType.packed], inputNames: ['A'], cacheHint: `${outputShape3D}` });
const createPackedReshape3DProgramInfo = (handler, input3D, metadata, outputShape3D) => {
const inputShape3D = input3D.dims;
const squeezedOutputShape = outputShape3D;
let mainLoop = '';
for (let i = 0; i < 4; i++) {
let outputCoords = '';
switch (i) {
case 0:
outputCoords = 'outputCoords = rc;';
break;
case 1:
outputCoords = 'outputCoords = ivec3(rc.x, rc.y+1, rc.z);';
break;
case 2:
outputCoords = 'outputCoords = ivec3(rc.x, rc.y, rc.z+1);';
break;
case 3:
outputCoords = 'outputCoords = ivec3(rc.x, rc.y+1, rc.z+1);';
break;
default:
throw new Error();
}
mainLoop += `
${outputCoords}
${i > 0 ? 'if(outputCoords.y < rows && outputCoords.z < cols){' : ''}
int flattenedIndex = getFlattenedIndex(outputCoords);
ivec3 inputRC = inputCoordsFromReshapedOutCoords(flattenedIndex);
vec2 innerDims = vec2(float(inputRC.y),float(inputRC.z));
result[${i}] = getChannel(getA(inputRC.x, inputRC.y, inputRC.z), innerDims);
${i > 0 ? '}' : ''}
`;
}
const glsl = (0, glsl_source_1.getGlsl)(handler.session.backend.glContext.version);
const shaderSource = `
${getReshapedInputCoords(inputShape3D)}
${getFlattenedIndexFrom3D(squeezedOutputShape)}
${(0, packing_utils_1.unpackFromChannel)()}
void main() {
ivec3 rc = getOutputCoords();
vec4 result = vec4(0.0);
ivec3 outputCoords;
int rows = ${squeezedOutputShape[2]};
int cols = ${squeezedOutputShape[1]};
${mainLoop}
${glsl.output} = result;
}
`;
return Object.assign(Object.assign({}, metadata), { output: { dims: squeezedOutputShape, type: input3D.type, textureType: types_1.TextureType.packed }, shaderSource, hasMain: true });
};
const createPackedReshape3DProgramInfoLoader = (handler, input3D, outputShape3D) => {
const metadata = createPackedReshape3DProgramMetadata(outputShape3D);
return Object.assign(Object.assign({}, metadata), { get: () => createPackedReshape3DProgramInfo(handler, input3D, metadata, outputShape3D) });
};
exports.createPackedReshape3DProgramInfoLoader = createPackedReshape3DProgramInfoLoader;
function processDims3D(shape) {
if (shape.length === 0) {
return [1, 1, 1];
}
// TODO: squeeze other shapes to 2D case
let batch = 1;
for (let i = 0; i < shape.length - 2; ++i) {
batch *= shape[i];
}
return [batch, shape.length > 1 ? shape[shape.length - 2] : 1, shape[shape.length - 1]];
}
exports.processDims3D = processDims3D;
// For packed reshape, we need to re-arrange texel data for output shape.
// Our pack is designed to pack a 2x2 tile in last h and w dimension, so
// for the reshaped new tensor, we just need to re-arrange the last h and
// w dimension. For any shape that is not in 3D, i.e. [batch, W, H], we
// first convert it to 3D by collapsing other dimension to batch dim, then
// process with the last two dimensions.
// Note: we only need the shape tensor to calculate output shape, so the
// content in shape tensor is never uploaded to GPU. It is always kept in CPU.
// TODO: optimize the algorithm -- in some cases, if the last two dims are
// the same between input shape and output shape, the packed reshape can be
// treated as no-op.
function isReshapeCheap(dims, reshapedDims) {
let isCheapReshape = false;
if (dims.length === 0 || reshapedDims.length === 0) { // scalar
isCheapReshape = true;
}
else if (dims.length < 2 || reshapedDims.length < 2) { // 1D
isCheapReshape = dims[dims.length - 1] === reshapedDims[reshapedDims.length - 1];
}
else { // 2D +
isCheapReshape = dims[dims.length - 1] === reshapedDims[reshapedDims.length - 1] &&
dims[dims.length - 2] === reshapedDims[reshapedDims.length - 2];
}
return isCheapReshape;
}
exports.isReshapeCheap = isReshapeCheap;
function getReshapedInputCoords(shape) {
const strides = util_1.ShapeUtil.computeStrides(shape);
const coords = ['b', 'r', 'c'];
const index = 'index';
const coordsFromIndexSnippet = strides
.map((stride, i) => {
const line1 = `int ${coords[i]} = ${index} / ${stride}`;
const line2 = i === strides.length - 1 ?
`int ${coords[i + 1]} = ${index} - ${coords[i]} * ${stride}` :
`index -= ${coords[i]} * ${stride}`;
return `${line1}; ${line2};`;
})
.join('');
return `
ivec3 inputCoordsFromReshapedOutCoords(int index) {
${coordsFromIndexSnippet}
return ivec3(b, r, c);
}
`;
}
function getFlattenedIndexFrom3D(shape) {
const strides = util_1.ShapeUtil.computeStrides(shape);
return `
int getFlattenedIndex(ivec3 coords) {
// reverse y, z order
return coords.x * ${strides[0]} + coords.z * ${strides[1]} + coords.y;
}
`;
}
//# sourceMappingURL=reshape-packed.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"reshape-packed.js","sourceRoot":"","sources":["reshape-packed.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,157 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import {Tensor} from '../../../tensor';
import {ShapeUtil} from '../../../util';
import {getGlsl} from '../glsl-source';
import {WebGLInferenceHandler} from '../inference-handler';
import {ProgramInfo, ProgramInfoLoader, ProgramMetadata, TextureType} from '../types';
import {unpackFromChannel} from './packing-utils';
const createPackedReshape3DProgramMetadata = (outputShape3D: readonly number[]) =>
({name: 'Reshape (packed)', inputTypes: [TextureType.packed], inputNames: ['A'], cacheHint: `${outputShape3D}`});
const createPackedReshape3DProgramInfo =
(handler: WebGLInferenceHandler, input3D: Tensor, metadata: ProgramMetadata, outputShape3D: readonly number[]):
ProgramInfo => {
const inputShape3D = input3D.dims as [number, number, number];
const squeezedOutputShape = outputShape3D as [number, number, number];
let mainLoop = '';
for (let i = 0; i < 4; i++) {
let outputCoords = '';
switch (i) {
case 0:
outputCoords = 'outputCoords = rc;';
break;
case 1:
outputCoords = 'outputCoords = ivec3(rc.x, rc.y+1, rc.z);';
break;
case 2:
outputCoords = 'outputCoords = ivec3(rc.x, rc.y, rc.z+1);';
break;
case 3:
outputCoords = 'outputCoords = ivec3(rc.x, rc.y+1, rc.z+1);';
break;
default:
throw new Error();
}
mainLoop += `
${outputCoords}
${i > 0 ? 'if(outputCoords.y < rows && outputCoords.z < cols){' : ''}
int flattenedIndex = getFlattenedIndex(outputCoords);
ivec3 inputRC = inputCoordsFromReshapedOutCoords(flattenedIndex);
vec2 innerDims = vec2(float(inputRC.y),float(inputRC.z));
result[${i}] = getChannel(getA(inputRC.x, inputRC.y, inputRC.z), innerDims);
${i > 0 ? '}' : ''}
`;
}
const glsl = getGlsl(handler.session.backend.glContext.version);
const shaderSource = `
${getReshapedInputCoords(inputShape3D)}
${getFlattenedIndexFrom3D(squeezedOutputShape)}
${unpackFromChannel()}
void main() {
ivec3 rc = getOutputCoords();
vec4 result = vec4(0.0);
ivec3 outputCoords;
int rows = ${squeezedOutputShape[2]};
int cols = ${squeezedOutputShape[1]};
${mainLoop}
${glsl.output} = result;
}
`;
return {
...metadata,
output: {dims: squeezedOutputShape, type: input3D.type, textureType: TextureType.packed},
shaderSource,
hasMain: true
};
};
export const createPackedReshape3DProgramInfoLoader =
(handler: WebGLInferenceHandler, input3D: Tensor, outputShape3D: readonly number[]): ProgramInfoLoader => {
const metadata = createPackedReshape3DProgramMetadata(outputShape3D);
return {...metadata, get: () => createPackedReshape3DProgramInfo(handler, input3D, metadata, outputShape3D)};
};
export function processDims3D(shape: ArrayLike<number>): [number, number, number] {
if (shape.length === 0) {
return [1, 1, 1];
}
// TODO: squeeze other shapes to 2D case
let batch = 1;
for (let i = 0; i < shape.length - 2; ++i) {
batch *= shape[i];
}
return [batch, shape.length > 1 ? shape[shape.length - 2] : 1, shape[shape.length - 1]];
}
// For packed reshape, we need to re-arrange texel data for output shape.
// Our pack is designed to pack a 2x2 tile in last h and w dimension, so
// for the reshaped new tensor, we just need to re-arrange the last h and
// w dimension. For any shape that is not in 3D, i.e. [batch, W, H], we
// first convert it to 3D by collapsing other dimension to batch dim, then
// process with the last two dimensions.
// Note: we only need the shape tensor to calculate output shape, so the
// content in shape tensor is never uploaded to GPU. It is always kept in CPU.
// TODO: optimize the algorithm -- in some cases, if the last two dims are
// the same between input shape and output shape, the packed reshape can be
// treated as no-op.
export function isReshapeCheap(dims: readonly number[], reshapedDims: readonly number[]) {
let isCheapReshape = false;
if (dims.length === 0 || reshapedDims.length === 0) { // scalar
isCheapReshape = true;
} else if (dims.length < 2 || reshapedDims.length < 2) { // 1D
isCheapReshape = dims[dims.length - 1] === reshapedDims[reshapedDims.length - 1];
} else { // 2D +
isCheapReshape = dims[dims.length - 1] === reshapedDims[reshapedDims.length - 1] &&
dims[dims.length - 2] === reshapedDims[reshapedDims.length - 2];
}
return isCheapReshape;
}
function getReshapedInputCoords(shape: [number, number, number]): string {
const strides = ShapeUtil.computeStrides(shape);
const coords = ['b', 'r', 'c'];
const index = 'index';
const coordsFromIndexSnippet = strides
.map((stride, i) => {
const line1 = `int ${coords[i]} = ${index} / ${stride}`;
const line2 = i === strides.length - 1 ?
`int ${coords[i + 1]} = ${index} - ${coords[i]} * ${stride}` :
`index -= ${coords[i]} * ${stride}`;
return `${line1}; ${line2};`;
})
.join('');
return `
ivec3 inputCoordsFromReshapedOutCoords(int index) {
${coordsFromIndexSnippet}
return ivec3(b, r, c);
}
`;
}
function getFlattenedIndexFrom3D(shape: [number, number, number]): string {
const strides = ShapeUtil.computeStrides(shape);
return `
int getFlattenedIndex(ivec3 coords) {
// reverse y, z order
return coords.x * ${strides[0]} + coords.z * ${strides[1]} + coords.y;
}
`;
}

View File

@@ -0,0 +1,17 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.reshape = void 0;
const util_1 = require("../../../util");
const reshape = (handler, inputs) => {
const reshapedDims = util_1.ShapeUtil.calculateReshapedDims(inputs[0].dims, inputs[1].integerData);
if (handler.session.pack) {
return [handler.reshapePacked(inputs[0], reshapedDims)];
}
else {
return [handler.reshapeUnpacked(inputs[0], reshapedDims)];
}
};
exports.reshape = reshape;
//# sourceMappingURL=reshape.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"reshape.js","sourceRoot":"","sources":["reshape.ts"],"names":[],"mappings":";AAAA,4DAA4D;AAC5D,kCAAkC;;;AAGlC,wCAAwC;AAGjC,MAAM,OAAO,GAAG,CAAC,OAA8B,EAAE,MAAgB,EAAY,EAAE;IACpF,MAAM,YAAY,GAAG,gBAAS,CAAC,qBAAqB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;IAC5F,IAAI,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE;QACxB,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC;KACzD;SAAM;QACL,OAAO,CAAC,OAAO,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC;KAC3D;AACH,CAAC,CAAC;AAPW,QAAA,OAAO,WAOlB"}

View File

@@ -0,0 +1,15 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import {Tensor} from '../../../tensor';
import {ShapeUtil} from '../../../util';
import {WebGLInferenceHandler} from '../inference-handler';
export const reshape = (handler: WebGLInferenceHandler, inputs: Tensor[]): Tensor[] => {
const reshapedDims = ShapeUtil.calculateReshapedDims(inputs[0].dims, inputs[1].integerData);
if (handler.session.pack) {
return [handler.reshapePacked(inputs[0], reshapedDims)];
} else {
return [handler.reshapeUnpacked(inputs[0], reshapedDims)];
}
};

View File

@@ -0,0 +1,238 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseResizeAttributesV11 = exports.parseResizeAttributesV10 = exports.resize = void 0;
const glsl_source_1 = require("../glsl-source");
const types_1 = require("../types");
const utils_1 = require("../utils");
const packing_utils_1 = require("./packing-utils");
const upsample_1 = require("./upsample");
const resizeProgramMetadata = {
name: 'Resize',
inputNames: ['A'],
inputTypes: [types_1.TextureType.packed]
};
const resize = (inferenceHandler, inputs, attributes) => {
(0, upsample_1.validateInputs)(inputs, attributes);
const output = inferenceHandler.run(Object.assign(Object.assign({}, resizeProgramMetadata), { cacheHint: attributes.cacheKey, get: () => createPackedResizeProgramInfo(inferenceHandler, inputs, attributes) }), inputs);
return [output];
};
exports.resize = resize;
const parseResizeAttributesV10 = (node) => (0, upsample_1.parseUpsampleAttributes)(node, 10);
exports.parseResizeAttributesV10 = parseResizeAttributesV10;
const parseResizeAttributesV11 = (node) => (0, upsample_1.parseUpsampleAttributes)(node, 11);
exports.parseResizeAttributesV11 = parseResizeAttributesV11;
const createPackedResizeProgramInfo = (inferenceHandler, inputs, attributes) => {
const glsl = (0, glsl_source_1.getGlsl)(inferenceHandler.session.backend.glContext.version);
const [scales, outputShape] = prepareInputs(inputs, attributes);
const isSame = scales.every((s) => s === 1) && attributes.coordinateTransformMode !== 'tf_crop_and_resize';
if (isSame) {
return Object.assign(Object.assign({}, resizeProgramMetadata), { output: { dims: outputShape, type: inputs[0].type, textureType: types_1.TextureType.packed }, hasMain: true, shaderSource: `void main() {
vec4 v = ${glsl.texture2D}(X, TexCoords);
${glsl.output} = v;
}` });
}
const dim = outputShape.length;
if (dim < 2) {
throw new Error(`output dimension should be at least 2, but got ${dim}`);
}
const outputHeight = outputShape[dim - 2];
const outputWidth = outputShape[dim - 1];
const inputShape = inputs[0].dims;
if (dim !== inputShape.length) {
throw new Error(`output dimension should match input ${inputShape.length}, but got ${dim}`);
}
const inputHeight = inputShape[dim - 2];
const inputWidth = inputShape[dim - 1];
const scalesHeight = scales[dim - 2];
const scalesWidth = scales[dim - 1];
let getSourceFracIndex = '';
if (attributes.mode !== 'linear') {
// TODO: support other modes
throw new Error(`resize (packed) does not support mode: '${attributes.mode}'`);
}
switch (attributes.coordinateTransformMode) {
case 'asymmetric':
getSourceFracIndex = `
vec4 getSourceFracIndex(ivec4 coords) {
return vec4(coords) / scaleWHWH;
}
`;
break;
case 'half_pixel':
getSourceFracIndex = `
vec4 getSourceFracIndex(ivec4 coords) {
return (vec4(coords) + 0.5) / scaleWHWH - 0.5;
}
`;
break;
case 'pytorch_half_pixel':
getSourceFracIndex = `
vec4 getSourceFracIndex(ivec4 coords) {
vec4 fcoords = vec4(coords);
return vec4(
${outputWidth}.0 > 1.0 ? (fcoords.x + 0.5) / scaleWHWH.x - 0.5 : 0.0,
${outputHeight}.0 > 1.0 ? (fcoords.y + 0.5) / scaleWHWH.y - 0.5 : 0.0,
${outputWidth}.0 > 1.0 ? (fcoords.z + 0.5) / scaleWHWH.z - 0.5 : 0.0,
${outputHeight}.0 > 1.0 ? (fcoords.w + 0.5) / scaleWHWH.w - 0.5 : 0.0
);
}
`;
break;
case 'align_corners':
getSourceFracIndex = `
vec4 getSourceFracIndex(ivec4 coords) {
vec4 resized = vec4(${outputWidth}.0 - 1.0, ${outputHeight}.0 - 1.0, ${outputWidth}.0 - 1.0,
${outputHeight}.0 - 1.0);
vec4 original = vec4(${inputWidth}.0 - 1.0, ${inputHeight}.0 - 1.0, ${inputWidth}.0 - 1.0,
${inputHeight}.0 - 1.0);
vec4 new_scale = original / resized;
return vec4(coords) * new_scale;
}
`;
break;
default:
// TODO:supporting other coordinateTransformModes
throw new Error(`resize (packed) does not support coordinateTransformMode: \
'${attributes.coordinateTransformMode}'`);
}
const coordsDataType = (0, utils_1.getCoordsDataType)(dim);
const unpackChannel = (0, packing_utils_1.unpackFromChannel)();
const shaderSource = `
const vec2 inputWH = vec2(${inputHeight}.0, ${inputWidth}.0);
const vec4 scaleWHWH = vec4(float(${scalesHeight}), float(${scalesWidth}), float(${scalesHeight}), float(${scalesWidth}));
${unpackChannel}
${getSourceFracIndex}
float getAValue(int x10, int r, int c, int d) {
return getChannel(getA(x10, r, c, d), vec2(c, d));
}
void main() {
${coordsDataType} rc = getOutputCoords();
int batch = rc[0];
int depth = rc[1];
// retrieve the 4 coordinates that is used in the 4 packed output values.
ivec4 coords = ivec4(rc.wz, rc.w + 1, rc.z + 1);
// calculate the source index in fraction
vec4 sourceFrac = getSourceFracIndex(coords);
// get the lower and upper bound of the 4 values that will be packed into one texel.
ivec4 x00 = ivec4(max(sourceFrac.xy, vec2(0.0)), min(inputWH - 1.0, ceil(sourceFrac.xy)));
ivec4 x01 = ivec4(max(sourceFrac.xw, vec2(0.0)), min(inputWH - 1.0, ceil(sourceFrac.xw)));
ivec4 x10 = ivec4(max(sourceFrac.zy, vec2(0.0)), min(inputWH - 1.0, ceil(sourceFrac.zy)));
ivec4 x11 = ivec4(max(sourceFrac.zw, vec2(0.0)), min(inputWH - 1.0, ceil(sourceFrac.zw)));
bool hasNextRow = rc.w < ${outputHeight - 1};
bool hasNextCol = rc.z < ${outputWidth - 1};
// pack x00, x01, x10, x11's top-left corner into one vec4 structure
vec4 topLeft = vec4(
getAValue(batch, depth, x00.x, x00.y),
hasNextCol ? getAValue(batch, depth, x01.x, x01.y) : 0.0,
hasNextRow ? getAValue(batch, depth, x10.x, x10.y) : 0.0,
(hasNextRow && hasNextCol) ? getAValue(batch, depth, x11.x, x11.y) : 0.0);
// pack x00, x01, x10, x11's top-right corner into one vec4 structure
vec4 topRight = vec4(
getAValue(batch, depth, x00.x, x00.w),
hasNextCol ? getAValue(batch, depth, x01.x, x01.w) : 0.0,
hasNextRow ? getAValue(batch, depth, x10.x, x10.w) : 0.0,
(hasNextRow && hasNextCol) ? getAValue(batch, depth, x11.x, x11.w) : 0.0);
// pack x00, x01, x10, x11's bottom-left corner into one vec4 structure
vec4 bottomLeft = vec4(
getAValue(batch, depth, x00.z, x00.y),
hasNextCol ? getAValue(batch, depth, x01.z, x01.y) : 0.0,
hasNextRow ? getAValue(batch, depth, x10.z, x10.y) : 0.0,
(hasNextRow && hasNextCol) ? getAValue(batch, depth, x11.z, x11.y) : 0.0);
// pack x00, x01, x10, x11's bottom-right corner into one vec4 structure
vec4 bottomRight = vec4(
getAValue(batch, depth, x00.z, x00.w),
hasNextCol ? getAValue(batch, depth, x01.z, x01.w) : 0.0,
hasNextRow ? getAValue(batch, depth, x10.z, x10.w) : 0.0,
(hasNextRow && hasNextCol) ? getAValue(batch, depth, x11.z, x11.w) : 0.0);
// calculate the interpolation fraction on u and v direction
vec4 frac = vec4(sourceFrac) - floor(sourceFrac);
vec4 clampFrac = clamp(frac, vec4(0.0), vec4(1.0));
vec4 top = mix(topLeft, topRight, clampFrac.ywyw);
vec4 bottom = mix(bottomLeft, bottomRight, clampFrac.ywyw);
vec4 newValue = mix(top, bottom, clampFrac.xxzz);
${glsl.output} = vec4(newValue);
}
`;
return Object.assign(Object.assign({}, resizeProgramMetadata), { output: { dims: outputShape, type: inputs[0].type, textureType: types_1.TextureType.packed }, hasMain: true, shaderSource });
};
const prepareInputs = (inputs, attributes) => {
const x = inputs[0];
const xDims = x.dims;
let scales = attributes.scales;
let outputSizes;
if (scales.length === 0) {
const scalesTensor = inputs[attributes.scalesInputIdx];
if (scalesTensor && scalesTensor.size !== 0) {
if (inputs[attributes.sizesInputIdx]) {
throw new Error('Only one of scales or sizes must be provided as input.');
}
scales = parseScalesData(scalesTensor, attributes.mode, attributes.isResize);
}
else {
const sizesTensor = inputs[attributes.sizesInputIdx];
if (!sizesTensor || sizesTensor.size === 0) {
throw new Error('Either scales or sizes MUST be provided as input.');
}
outputSizes = Array.from(sizesTensor.integerData);
scales = parseScalesDataFromOutputSize(outputSizes, xDims, attributes.mode, attributes.isResize);
}
}
else {
if (inputs[attributes.sizesInputIdx]) {
throw new Error('Only one of scales or sizes must be provided as input.');
}
}
const yDims = outputSizes || (xDims.map((dim, i) => Math.floor(dim * scales[i])));
return [scales, yDims];
};
const parseScalesData = (scale, mode, isResize) => {
const scales = Array.from(scale.floatData);
(0, upsample_1.scalesValidation)(scales, mode, isResize);
return scales;
};
const parseScalesDataFromOutputSize = (yDims, xDims, mode, isResize) => {
const length = xDims.length;
const scales = new Array(length);
for (let i = 0, end = length; i < end; i++) {
if (xDims[i] === 0) {
if (yDims[i] !== 0) {
throw new Error('Input dim is zero but required output dim is non-zero.');
}
scales[i] = 1;
}
else {
scales[i] = yDims[i] / xDims[i];
}
}
(0, upsample_1.scalesValidation)(scales, mode, isResize);
return scales;
};
// roi data is not used yet. but leave here for future usage.
// const getRoi = (inputs: Tensor[], attributes: UpsampleAttributes) : number[] => {
// let roi: number[] = [];
// if (attributes.needRoiInput) {
// if (attributes.roiInputIdx <= 0) {
// throw new Error('Invalid roi input index.');
// }
// const roiTensor = inputs[attributes.roiInputIdx];
// roi = roiTensor.size > 0 ? Array.from(roiTensor.floatData) : [];
// } else {
// roi = new Array(inputs[0].dims.length * 2).fill(0);
// }
// return roi;
// };
//# sourceMappingURL=resize-packed.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,280 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import {Graph} from '../../../graph';
import {OperatorImplementation, OperatorInitialization} from '../../../operators';
import {Tensor} from '../../../tensor';
import {getGlsl} from '../glsl-source';
import {WebGLInferenceHandler} from '../inference-handler';
import {ProgramInfo, TextureType} from '../types';
import {getCoordsDataType} from '../utils';
import {unpackFromChannel} from './packing-utils';
import {parseUpsampleAttributes, scalesValidation, UpsampleAttributes, validateInputs} from './upsample';
const resizeProgramMetadata = {
name: 'Resize',
inputNames: ['A'],
inputTypes: [TextureType.packed]
};
export const resize: OperatorImplementation<UpsampleAttributes> =
(inferenceHandler: WebGLInferenceHandler, inputs: Tensor[], attributes: UpsampleAttributes): Tensor[] => {
validateInputs(inputs, attributes);
const output = inferenceHandler.run(
{
...resizeProgramMetadata,
cacheHint: attributes.cacheKey,
get: () => createPackedResizeProgramInfo(inferenceHandler, inputs, attributes)
},
inputs);
return [output];
};
export const parseResizeAttributesV10: OperatorInitialization<UpsampleAttributes> =
(node: Graph.Node): UpsampleAttributes => parseUpsampleAttributes(node, 10);
export const parseResizeAttributesV11: OperatorInitialization<UpsampleAttributes> =
(node: Graph.Node): UpsampleAttributes => parseUpsampleAttributes(node, 11);
const createPackedResizeProgramInfo =
(inferenceHandler: WebGLInferenceHandler, inputs: Tensor[], attributes: UpsampleAttributes): ProgramInfo => {
const glsl = getGlsl(inferenceHandler.session.backend.glContext.version);
const [scales, outputShape] = prepareInputs(inputs, attributes);
const isSame =
scales.every((s: number) => s === 1) && attributes.coordinateTransformMode !== 'tf_crop_and_resize';
if (isSame) {
return {
...resizeProgramMetadata,
output: {dims: outputShape, type: inputs[0].type, textureType: TextureType.packed},
hasMain: true,
shaderSource: `void main() {
vec4 v = ${glsl.texture2D}(X, TexCoords);
${glsl.output} = v;
}`
};
}
const dim = outputShape.length;
if (dim < 2) {
throw new Error(`output dimension should be at least 2, but got ${dim}`);
}
const outputHeight = outputShape[dim - 2];
const outputWidth = outputShape[dim - 1];
const inputShape = inputs[0].dims;
if (dim !== inputShape.length) {
throw new Error(`output dimension should match input ${inputShape.length}, but got ${dim}`);
}
const inputHeight = inputShape[dim - 2];
const inputWidth = inputShape[dim - 1];
const scalesHeight = scales[dim - 2];
const scalesWidth = scales[dim - 1];
let getSourceFracIndex = '';
if (attributes.mode !== 'linear') {
// TODO: support other modes
throw new Error(`resize (packed) does not support mode: '${attributes.mode}'`);
}
switch (attributes.coordinateTransformMode) {
case 'asymmetric':
getSourceFracIndex = `
vec4 getSourceFracIndex(ivec4 coords) {
return vec4(coords) / scaleWHWH;
}
`;
break;
case 'half_pixel':
getSourceFracIndex = `
vec4 getSourceFracIndex(ivec4 coords) {
return (vec4(coords) + 0.5) / scaleWHWH - 0.5;
}
`;
break;
case 'pytorch_half_pixel':
getSourceFracIndex = `
vec4 getSourceFracIndex(ivec4 coords) {
vec4 fcoords = vec4(coords);
return vec4(
${outputWidth}.0 > 1.0 ? (fcoords.x + 0.5) / scaleWHWH.x - 0.5 : 0.0,
${outputHeight}.0 > 1.0 ? (fcoords.y + 0.5) / scaleWHWH.y - 0.5 : 0.0,
${outputWidth}.0 > 1.0 ? (fcoords.z + 0.5) / scaleWHWH.z - 0.5 : 0.0,
${outputHeight}.0 > 1.0 ? (fcoords.w + 0.5) / scaleWHWH.w - 0.5 : 0.0
);
}
`;
break;
case 'align_corners':
getSourceFracIndex = `
vec4 getSourceFracIndex(ivec4 coords) {
vec4 resized = vec4(${outputWidth}.0 - 1.0, ${outputHeight}.0 - 1.0, ${outputWidth}.0 - 1.0,
${outputHeight}.0 - 1.0);
vec4 original = vec4(${inputWidth}.0 - 1.0, ${inputHeight}.0 - 1.0, ${inputWidth}.0 - 1.0,
${inputHeight}.0 - 1.0);
vec4 new_scale = original / resized;
return vec4(coords) * new_scale;
}
`;
break;
default:
// TODO:supporting other coordinateTransformModes
throw new Error(`resize (packed) does not support coordinateTransformMode: \
'${attributes.coordinateTransformMode}'`);
}
const coordsDataType = getCoordsDataType(dim);
const unpackChannel = unpackFromChannel();
const shaderSource = `
const vec2 inputWH = vec2(${inputHeight}.0, ${inputWidth}.0);
const vec4 scaleWHWH = vec4(float(${scalesHeight}), float(${scalesWidth}), float(${scalesHeight}), float(${
scalesWidth}));
${unpackChannel}
${getSourceFracIndex}
float getAValue(int x10, int r, int c, int d) {
return getChannel(getA(x10, r, c, d), vec2(c, d));
}
void main() {
${coordsDataType} rc = getOutputCoords();
int batch = rc[0];
int depth = rc[1];
// retrieve the 4 coordinates that is used in the 4 packed output values.
ivec4 coords = ivec4(rc.wz, rc.w + 1, rc.z + 1);
// calculate the source index in fraction
vec4 sourceFrac = getSourceFracIndex(coords);
// get the lower and upper bound of the 4 values that will be packed into one texel.
ivec4 x00 = ivec4(max(sourceFrac.xy, vec2(0.0)), min(inputWH - 1.0, ceil(sourceFrac.xy)));
ivec4 x01 = ivec4(max(sourceFrac.xw, vec2(0.0)), min(inputWH - 1.0, ceil(sourceFrac.xw)));
ivec4 x10 = ivec4(max(sourceFrac.zy, vec2(0.0)), min(inputWH - 1.0, ceil(sourceFrac.zy)));
ivec4 x11 = ivec4(max(sourceFrac.zw, vec2(0.0)), min(inputWH - 1.0, ceil(sourceFrac.zw)));
bool hasNextRow = rc.w < ${outputHeight - 1};
bool hasNextCol = rc.z < ${outputWidth - 1};
// pack x00, x01, x10, x11's top-left corner into one vec4 structure
vec4 topLeft = vec4(
getAValue(batch, depth, x00.x, x00.y),
hasNextCol ? getAValue(batch, depth, x01.x, x01.y) : 0.0,
hasNextRow ? getAValue(batch, depth, x10.x, x10.y) : 0.0,
(hasNextRow && hasNextCol) ? getAValue(batch, depth, x11.x, x11.y) : 0.0);
// pack x00, x01, x10, x11's top-right corner into one vec4 structure
vec4 topRight = vec4(
getAValue(batch, depth, x00.x, x00.w),
hasNextCol ? getAValue(batch, depth, x01.x, x01.w) : 0.0,
hasNextRow ? getAValue(batch, depth, x10.x, x10.w) : 0.0,
(hasNextRow && hasNextCol) ? getAValue(batch, depth, x11.x, x11.w) : 0.0);
// pack x00, x01, x10, x11's bottom-left corner into one vec4 structure
vec4 bottomLeft = vec4(
getAValue(batch, depth, x00.z, x00.y),
hasNextCol ? getAValue(batch, depth, x01.z, x01.y) : 0.0,
hasNextRow ? getAValue(batch, depth, x10.z, x10.y) : 0.0,
(hasNextRow && hasNextCol) ? getAValue(batch, depth, x11.z, x11.y) : 0.0);
// pack x00, x01, x10, x11's bottom-right corner into one vec4 structure
vec4 bottomRight = vec4(
getAValue(batch, depth, x00.z, x00.w),
hasNextCol ? getAValue(batch, depth, x01.z, x01.w) : 0.0,
hasNextRow ? getAValue(batch, depth, x10.z, x10.w) : 0.0,
(hasNextRow && hasNextCol) ? getAValue(batch, depth, x11.z, x11.w) : 0.0);
// calculate the interpolation fraction on u and v direction
vec4 frac = vec4(sourceFrac) - floor(sourceFrac);
vec4 clampFrac = clamp(frac, vec4(0.0), vec4(1.0));
vec4 top = mix(topLeft, topRight, clampFrac.ywyw);
vec4 bottom = mix(bottomLeft, bottomRight, clampFrac.ywyw);
vec4 newValue = mix(top, bottom, clampFrac.xxzz);
${glsl.output} = vec4(newValue);
}
`;
return {
...resizeProgramMetadata,
output: {dims: outputShape, type: inputs[0].type, textureType: TextureType.packed},
hasMain: true,
shaderSource
};
};
const prepareInputs = (inputs: Tensor[], attributes: UpsampleAttributes): [readonly number[], readonly number[]] => {
const x = inputs[0];
const xDims = x.dims;
let scales = attributes.scales;
let outputSizes: number[]|undefined;
if (scales.length === 0) {
const scalesTensor = inputs[attributes.scalesInputIdx];
if (scalesTensor && scalesTensor.size !== 0) {
if (inputs[attributes.sizesInputIdx]) {
throw new Error('Only one of scales or sizes must be provided as input.');
}
scales = parseScalesData(scalesTensor, attributes.mode, attributes.isResize);
} else {
const sizesTensor = inputs[attributes.sizesInputIdx];
if (!sizesTensor || sizesTensor.size === 0) {
throw new Error('Either scales or sizes MUST be provided as input.');
}
outputSizes = Array.from(sizesTensor.integerData);
scales = parseScalesDataFromOutputSize(outputSizes, xDims, attributes.mode, attributes.isResize);
}
} else {
if (inputs[attributes.sizesInputIdx]) {
throw new Error('Only one of scales or sizes must be provided as input.');
}
}
const yDims = outputSizes || (xDims.map((dim, i) => Math.floor(dim * scales[i])));
return [scales, yDims];
};
const parseScalesData = (scale: Tensor, mode: string, isResize: boolean): number[] => {
const scales = Array.from(scale.floatData);
scalesValidation(scales, mode, isResize);
return scales;
};
const parseScalesDataFromOutputSize =
(yDims: readonly number[], xDims: readonly number[], mode: string, isResize: boolean): number[] => {
const length = xDims.length;
const scales = new Array<number>(length);
for (let i = 0, end = length; i < end; i++) {
if (xDims[i] === 0) {
if (yDims[i] !== 0) {
throw new Error('Input dim is zero but required output dim is non-zero.');
}
scales[i] = 1;
} else {
scales[i] = yDims[i] / xDims[i];
}
}
scalesValidation(scales, mode, isResize);
return scales;
};
// roi data is not used yet. but leave here for future usage.
// const getRoi = (inputs: Tensor[], attributes: UpsampleAttributes) : number[] => {
// let roi: number[] = [];
// if (attributes.needRoiInput) {
// if (attributes.roiInputIdx <= 0) {
// throw new Error('Invalid roi input index.');
// }
// const roiTensor = inputs[attributes.roiInputIdx];
// roi = roiTensor.size > 0 ? Array.from(roiTensor.floatData) : [];
// } else {
// roi = new Array(inputs[0].dims.length * 2).fill(0);
// }
// return roi;
// };

View File

@@ -0,0 +1,17 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.shape = void 0;
const tensor_1 = require("../../../tensor");
const shape = (inferenceHandler, inputs) => {
validateInputs(inputs);
return [new tensor_1.Tensor([inputs[0].dims.length], 'int32', undefined, undefined, new Int32Array(inputs[0].dims))];
};
exports.shape = shape;
const validateInputs = (inputs) => {
if (!inputs || inputs.length !== 1) {
throw new Error('Shape requires 1 input.');
}
};
//# sourceMappingURL=shape.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"shape.js","sourceRoot":"","sources":["shape.ts"],"names":[],"mappings":";AAAA,4DAA4D;AAC5D,kCAAkC;;;AAElC,4CAAuC;AAGhC,MAAM,KAAK,GAAG,CAAC,gBAAuC,EAAE,MAAgB,EAAY,EAAE;IAC3F,cAAc,CAAC,MAAM,CAAC,CAAC;IACvB,OAAO,CAAC,IAAI,eAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAC9G,CAAC,CAAC;AAHW,QAAA,KAAK,SAGhB;AAEF,MAAM,cAAc,GAAG,CAAC,MAAgB,EAAQ,EAAE;IAChD,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE;QAClC,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;KAC5C;AACH,CAAC,CAAC"}

View File

@@ -0,0 +1,16 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import {Tensor} from '../../../tensor';
import {WebGLInferenceHandler} from '../inference-handler';
export const shape = (inferenceHandler: WebGLInferenceHandler, inputs: Tensor[]): Tensor[] => {
validateInputs(inputs);
return [new Tensor([inputs[0].dims.length], 'int32', undefined, undefined, new Int32Array(inputs[0].dims))];
};
const validateInputs = (inputs: Tensor[]): void => {
if (!inputs || inputs.length !== 1) {
throw new Error('Shape requires 1 input.');
}
};

View File

@@ -0,0 +1,107 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.sliceV10 = exports.parseSliceAttributes = exports.slice = void 0;
const attribute_with_cache_key_1 = require("../../../attribute-with-cache-key");
const operators_1 = require("../../../operators");
const util_1 = require("../../../util");
const types_1 = require("../types");
const sliceProgramMetadata = {
name: 'Slice',
inputNames: ['A'],
inputTypes: [types_1.TextureType.unpacked]
};
const slice = (inferenceHandler, inputs, attributes) => {
validateInputs(inputs);
const output = inferenceHandler.run(Object.assign(Object.assign({}, sliceProgramMetadata), { cacheHint: attributes.cacheKey, get: () => createSliceProgramInfo(inferenceHandler, inputs[0], attributes) }), inputs);
return [output];
};
exports.slice = slice;
const parseSliceAttributes = (node) => {
const starts = node.attributes.getInts('starts');
const ends = node.attributes.getInts('ends');
const axes = node.attributes.getInts('axes', []);
return (0, attribute_with_cache_key_1.createAttributeWithCacheKey)({ starts, ends, axes });
};
exports.parseSliceAttributes = parseSliceAttributes;
const createSliceProgramInfo = (inferenceHandler, input, attributes) => {
const axes = (attributes.axes.length === 0) ? input.dims.slice(0).map((val, i) => i) : attributes.axes;
const normalizedAxes = util_1.ShapeUtil.normalizeAxes(axes, input.dims.length);
const starts = attributes.starts.map((start, i) => {
if (start > input.dims[normalizedAxes[i]] - 1) {
return input.dims[normalizedAxes[i]];
}
return util_1.ShapeUtil.normalizeAxis(start, input.dims[normalizedAxes[i]]);
});
const ends = attributes.ends.map((end, i) => {
if (end > input.dims[normalizedAxes[i]] - 1) {
return input.dims[normalizedAxes[i]];
}
return util_1.ShapeUtil.normalizeAxis(end, input.dims[normalizedAxes[i]]);
});
const outputShape = input.dims.slice();
const sliceOps = [];
for (let i = 0; i < normalizedAxes.length; i++) {
outputShape[normalizedAxes[i]] = ends[i] - starts[i];
if (starts[i] > 0) {
sliceOps.push(`outputIdx[${normalizedAxes[i]}] += ${starts[i]};`);
} // else { sliceOps.push(`outputIdx[${normalizedAxes[i]}] += 0;`); }
}
const rank = outputShape.length;
const shaderSource = `
float process(int outputIdx[${rank}]) {
${sliceOps.join('\n ')}
return _A(outputIdx);
}`;
return Object.assign(Object.assign({}, sliceProgramMetadata), { output: { dims: outputShape, type: input.type, textureType: types_1.TextureType.unpacked }, shaderSource });
};
const validateInputs = (inputs) => {
if (!inputs || inputs.length !== 1) {
throw new Error('Slice requires 1 input.');
}
if (operators_1.NUMBER_TYPES.indexOf(inputs[0].type) === -1) {
throw new Error('Invalid input type.');
}
};
const sliceV10 = (inferenceHandler, inputs) => {
validateInputsV10(inputs);
const attributes = generateSliceAttributesFromInputs(inferenceHandler, inputs);
const output = inferenceHandler.run(Object.assign(Object.assign({}, sliceProgramMetadata), { cacheHint: attributes.cacheKey, get: () => createSliceProgramInfo(inferenceHandler, inputs[0], attributes) }), [inputs[0]]);
return [output];
};
exports.sliceV10 = sliceV10;
const generateSliceAttributesFromInputs = (inferenceHandler, inputs) => {
if (!inferenceHandler.session.isInitializer(inputs[1].dataId) ||
!inferenceHandler.session.isInitializer(inputs[2].dataId) ||
(inputs.length >= 4 && !inferenceHandler.session.isInitializer(inputs[3].dataId)) ||
(inputs.length >= 5 && !inferenceHandler.session.isInitializer(inputs[4].dataId))) {
throw new Error('dynamic slice attributes are not allowed');
}
if (inputs.length >= 5 && inputs[4].integerData.some((i) => i !== 1)) {
throw new Error('currently non-1 steps is not supported for Slice');
}
const starts = Array.from(inputs[1].integerData);
const ends = Array.from(inputs[2].integerData);
const axes = inputs.length >= 4 ? Array.from(inputs[3].integerData) : [];
const cacheKey = `${axes};${starts};${ends}`;
return { starts, ends, axes, cacheKey };
};
const validateInputsV10 = (inputs) => {
if (!inputs || inputs.length < 3 || inputs.length > 5) {
throw new Error('Invalid input number.');
}
if (inputs[1].type !== 'int32' || inputs[1].dims.length !== 1) {
throw new Error('Invalid input type.');
}
if (inputs[2].type !== 'int32' || inputs[2].dims.length !== 1) {
throw new Error('Invalid input type.');
}
if (inputs.length >= 4 && (inputs[3].type !== 'int32' || inputs[3].dims.length !== 1)) {
throw new Error('Invalid input type.');
}
if (inputs.length >= 5 && (inputs[4].type !== 'int32' || inputs[4].dims.length !== 1)) {
throw new Error('Invalid input type.');
}
};
//# sourceMappingURL=slice.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,142 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import {AttributeWithCacheKey, createAttributeWithCacheKey} from '../../../attribute-with-cache-key';
import {Graph} from '../../../graph';
import {NUMBER_TYPES, OperatorImplementation, OperatorInitialization} from '../../../operators';
import {Tensor} from '../../../tensor';
import {ShapeUtil} from '../../../util';
import {WebGLInferenceHandler} from '../inference-handler';
import {ProgramInfo, TextureType} from '../types';
export interface SliceAttributes extends AttributeWithCacheKey {
readonly axes: number[];
readonly ends: number[];
readonly starts: number[];
}
const sliceProgramMetadata = {
name: 'Slice',
inputNames: ['A'],
inputTypes: [TextureType.unpacked]
};
export const slice: OperatorImplementation<SliceAttributes> =
(inferenceHandler: WebGLInferenceHandler, inputs: Tensor[], attributes: SliceAttributes): Tensor[] => {
validateInputs(inputs);
const output = inferenceHandler.run(
{
...sliceProgramMetadata,
cacheHint: attributes.cacheKey,
get: () => createSliceProgramInfo(inferenceHandler, inputs[0], attributes)
},
inputs);
return [output];
};
export const parseSliceAttributes: OperatorInitialization<SliceAttributes> = (node: Graph.Node): SliceAttributes => {
const starts = node.attributes.getInts('starts');
const ends = node.attributes.getInts('ends');
const axes = node.attributes.getInts('axes', []);
return createAttributeWithCacheKey({starts, ends, axes});
};
const createSliceProgramInfo =
(inferenceHandler: WebGLInferenceHandler, input: Tensor, attributes: SliceAttributes): ProgramInfo => {
const axes = (attributes.axes.length === 0) ? input.dims.slice(0).map((val, i) => i) : attributes.axes;
const normalizedAxes = ShapeUtil.normalizeAxes(axes, input.dims.length);
const starts = attributes.starts.map((start, i) => {
if (start > input.dims[normalizedAxes[i]] - 1) {
return input.dims[normalizedAxes[i]];
}
return ShapeUtil.normalizeAxis(start, input.dims[normalizedAxes[i]]);
});
const ends = attributes.ends.map((end, i) => {
if (end > input.dims[normalizedAxes[i]] - 1) {
return input.dims[normalizedAxes[i]];
}
return ShapeUtil.normalizeAxis(end, input.dims[normalizedAxes[i]]);
});
const outputShape = input.dims.slice();
const sliceOps: string[] = [];
for (let i = 0; i < normalizedAxes.length; i++) {
outputShape[normalizedAxes[i]] = ends[i] - starts[i];
if (starts[i] > 0) {
sliceOps.push(`outputIdx[${normalizedAxes[i]}] += ${starts[i]};`);
} // else { sliceOps.push(`outputIdx[${normalizedAxes[i]}] += 0;`); }
}
const rank = outputShape.length;
const shaderSource = `
float process(int outputIdx[${rank}]) {
${sliceOps.join('\n ')}
return _A(outputIdx);
}`;
return {
...sliceProgramMetadata,
output: {dims: outputShape, type: input.type, textureType: TextureType.unpacked},
shaderSource
};
};
const validateInputs = (inputs: Tensor[]): void => {
if (!inputs || inputs.length !== 1) {
throw new Error('Slice requires 1 input.');
}
if (NUMBER_TYPES.indexOf(inputs[0].type) === -1) {
throw new Error('Invalid input type.');
}
};
export const sliceV10 = (inferenceHandler: WebGLInferenceHandler, inputs: Tensor[]): Tensor[] => {
validateInputsV10(inputs);
const attributes = generateSliceAttributesFromInputs(inferenceHandler, inputs);
const output = inferenceHandler.run(
{
...sliceProgramMetadata,
cacheHint: attributes.cacheKey,
get: () => createSliceProgramInfo(inferenceHandler, inputs[0], attributes)
},
[inputs[0]]);
return [output];
};
const generateSliceAttributesFromInputs =
(inferenceHandler: WebGLInferenceHandler, inputs: Tensor[]): SliceAttributes => {
if (!inferenceHandler.session.isInitializer(inputs[1].dataId) ||
!inferenceHandler.session.isInitializer(inputs[2].dataId) ||
(inputs.length >= 4 && !inferenceHandler.session.isInitializer(inputs[3].dataId)) ||
(inputs.length >= 5 && !inferenceHandler.session.isInitializer(inputs[4].dataId))) {
throw new Error('dynamic slice attributes are not allowed');
}
if (inputs.length >= 5 && inputs[4].integerData.some((i: number) => i !== 1)) {
throw new Error('currently non-1 steps is not supported for Slice');
}
const starts = Array.from(inputs[1].integerData);
const ends = Array.from(inputs[2].integerData);
const axes = inputs.length >= 4 ? Array.from(inputs[3].integerData) : [];
const cacheKey = `${axes};${starts};${ends}`;
return {starts, ends, axes, cacheKey};
};
const validateInputsV10 = (inputs: Tensor[]): void => {
if (!inputs || inputs.length < 3 || inputs.length > 5) {
throw new Error('Invalid input number.');
}
if (inputs[1].type !== 'int32' || inputs[1].dims.length !== 1) {
throw new Error('Invalid input type.');
}
if (inputs[2].type !== 'int32' || inputs[2].dims.length !== 1) {
throw new Error('Invalid input type.');
}
if (inputs.length >= 4 && (inputs[3].type !== 'int32' || inputs[3].dims.length !== 1)) {
throw new Error('Invalid input type.');
}
if (inputs.length >= 5 && (inputs[4].type !== 'int32' || inputs[4].dims.length !== 1)) {
throw new Error('Invalid input type.');
}
};

View File

@@ -0,0 +1,202 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.softmaxV13 = exports.parseSoftmaxAttributesV13 = exports.parseSoftmaxAttributes = exports.softmax = void 0;
const attribute_with_cache_key_1 = require("../../../attribute-with-cache-key");
const util_1 = require("../../../util");
const glsl_source_1 = require("../glsl-source");
const types_1 = require("../types");
const transpose_1 = require("./transpose");
const softmaxComputeMaxProgramMetadata = {
name: 'SoftmaxComputeMax',
inputNames: ['A'],
inputTypes: [types_1.TextureType.unpacked],
};
const softmaxComputeScaleProgramMetadata = {
name: 'SoftmaxComputeScale',
inputNames: ['A', 'Max'],
inputTypes: [types_1.TextureType.unpacked, types_1.TextureType.unpacked],
};
const softmaxProgramMetadata = {
name: 'SoftMax',
inputNames: ['A', 'Max', 'Norm'],
inputTypes: [types_1.TextureType.unpacked, types_1.TextureType.unpacked, types_1.TextureType.unpacked],
};
const softmax = (inferenceHandler, inputs, attributes) => {
validateInputs(inputs);
const inputShape = inputs[0].dims.slice();
const axis = util_1.ShapeUtil.normalizeAxis(attributes.axis, inputShape.length);
const logicalRowCount = util_1.ShapeUtil.sizeToDimension(inputShape, axis);
const featureCount = util_1.ShapeUtil.sizeFromDimension(inputShape, axis);
const output = computeSoftmax(inferenceHandler, inputs, attributes, logicalRowCount, featureCount);
return output;
};
exports.softmax = softmax;
const parseSoftmaxAttributes = (node) => (0, attribute_with_cache_key_1.createAttributeWithCacheKey)({ axis: node.attributes.getInt('axis', 1) });
exports.parseSoftmaxAttributes = parseSoftmaxAttributes;
const parseSoftmaxAttributesV13 = (node) => (0, attribute_with_cache_key_1.createAttributeWithCacheKey)({ axis: node.attributes.getInt('axis', -1) });
exports.parseSoftmaxAttributesV13 = parseSoftmaxAttributesV13;
// The "semantic" meaning of axis has changed in opset-13.
// Please compare: https://github.com/onnx/onnx/blob/main/docs/Operators.md#Softmax
// with https://github.com/onnx/onnx/blob/main/docs/Changelog.md#Softmax-11 for detailed explanations
// To account for the opset-13 behavior, our plan will be to transpose the "axis" dim to the innermost dim
// and perform softmax and then reverse the transpose. We can skip the transposing aspect if the axis is already
// the innermost dim
const softmaxV13 = (inferenceHandler, inputs, attributes) => {
validateInputs(inputs);
const inputShape = inputs[0].dims.slice();
const axis = util_1.ShapeUtil.normalizeAxis(attributes.axis, inputShape.length);
const rank = inputShape.length;
const isTransposeRequired = (axis !== rank - 1) ? true : false;
const transposedInputShape = [];
let perm = [];
let transposedInputs = [];
let transposeAttribute;
if (isTransposeRequired) {
perm = Array.from({ length: rank }).map((_, i) => i);
// swap the innermost dim with the dim corresponding to axis
perm[axis] = rank - 1;
perm[rank - 1] = axis;
perm.map(p => transposedInputShape.push(inputShape[p]));
transposeAttribute = (0, attribute_with_cache_key_1.createAttributeWithCacheKey)({ perm });
transposedInputs = (0, transpose_1.transpose)(inferenceHandler, inputs, transposeAttribute);
}
const logicalRowCount = isTransposeRequired ? util_1.ShapeUtil.sizeToDimension(transposedInputShape, rank - 1) :
util_1.ShapeUtil.sizeToDimension(inputShape, rank - 1);
const featureCount = isTransposeRequired ? util_1.ShapeUtil.sizeFromDimension(transposedInputShape, rank - 1) :
util_1.ShapeUtil.sizeFromDimension(inputShape, rank - 1);
const output = computeSoftmax(inferenceHandler, isTransposeRequired ? transposedInputs : inputs, attributes, logicalRowCount, featureCount);
if (isTransposeRequired) {
const reversedOutput = (0, transpose_1.transpose)(inferenceHandler, output, transposeAttribute);
return reversedOutput;
}
else {
return output;
}
};
exports.softmaxV13 = softmaxV13;
const computeSoftmax = (inferenceHandler, inputs, attributes, logicalRowCount, featureCount) => {
const computeMaxProgramInfo = createComputeMaxProgramInfo(inferenceHandler, inputs[0], logicalRowCount, featureCount, [logicalRowCount]);
const max = inferenceHandler.run(Object.assign(Object.assign({}, softmaxComputeMaxProgramMetadata), { cacheHint: attributes.cacheKey, get: () => computeMaxProgramInfo }), inputs);
const computeScaleProgramInfo = createComputScaleProgramInfo(inferenceHandler, inputs[0], logicalRowCount, featureCount, computeMaxProgramInfo.output.dims, [logicalRowCount]);
const scale = inferenceHandler.run(Object.assign(Object.assign({}, softmaxComputeScaleProgramMetadata), { cacheHint: attributes.cacheKey, get: () => computeScaleProgramInfo }), [inputs[0], max]);
const softMaxProgramInfo = createSoftMaxProgramInfo(inferenceHandler, inputs[0], logicalRowCount, featureCount, computeMaxProgramInfo.output.dims, computeScaleProgramInfo.output.dims);
const output = inferenceHandler.run(Object.assign(Object.assign({}, softmaxProgramMetadata), { cacheHint: attributes.cacheKey, get: () => softMaxProgramInfo }), [inputs[0], max, scale]);
return [output];
};
/**
* Create a texture that contains the maximum value of each of the 'N' rows
*/
const createComputeMaxProgramInfo = (inferenceHandler, input, logicalRowCount, featureCount, outputShape) => {
const [textureWidth, textureHeight] = inferenceHandler.calculateTextureWidthAndHeight(input.dims, types_1.TextureType.unpacked);
const rank = outputShape.length;
if (logicalRowCount < 1 || featureCount < 1) {
throw new Error('Logical row count N and feature count D must be greater than or equal to 1');
}
if (outputShape.length !== 1) {
throw new Error('Dimensionality of the output should be 1');
}
if (outputShape[0] !== logicalRowCount) {
throw new Error('Shape of the output should be equal to logical row count');
}
const glsl = (0, glsl_source_1.getGlsl)(inferenceHandler.session.backend.glContext.version);
const shaderSource = `
float process(int[${rank}] indices) {
int logical_row_start_offset = indices[0] * ${featureCount};
float max = getColorAsFloat(${glsl.texture2D}(A, offsetToCoords(logical_row_start_offset, ${textureWidth},
${textureHeight} )));
for(int i=1; i<${featureCount}; ++i)
{
float current = getColorAsFloat(${glsl.texture2D}(A, offsetToCoords(logical_row_start_offset + i,
${textureWidth}, ${textureHeight})));
if(current > max)
max = current;
}
return max;
}`;
return Object.assign(Object.assign({}, softmaxComputeMaxProgramMetadata), { output: { dims: outputShape, type: input.type, textureType: types_1.TextureType.unpacked }, shaderSource });
};
/**
* Create a texture that contains the normalization factor for each of the 'N' rows
*/
const createComputScaleProgramInfo = (inferenceHandler, input, logicalRowCount, featureCount, maxElementPerLogicalRow, outputShape) => {
const [textureWidth, textureHeight] = inferenceHandler.calculateTextureWidthAndHeight(input.dims, types_1.TextureType.unpacked);
const rank = outputShape.length;
if (logicalRowCount < 1 || featureCount < 1) {
throw new Error('Logical row count N and feature count D must be greater than or equal to 1');
}
if (outputShape.length !== 1) {
throw new Error('Dimensionality of the output should be 1');
}
if (outputShape[0] !== logicalRowCount) {
throw new Error('Shape of the output should be equal to logical row count');
}
if (maxElementPerLogicalRow.length !== 1) {
throw new Error('Dimensionality of the intermediate results should be 1');
}
if (maxElementPerLogicalRow[0] !== logicalRowCount) {
throw new Error('Shape of the intermediate results should be equal to logical row count');
}
const glsl = (0, glsl_source_1.getGlsl)(inferenceHandler.session.backend.glContext.version);
const shaderSource = `
float process(int[${rank}] indices) {
int logical_row_start_offset = indices[0] * ${featureCount};
float norm_factor = 0.0;
float max = _Max(indices);
for(int i=0; i<${featureCount}; ++i)
{
norm_factor += exp(getColorAsFloat(${glsl.texture2D}(A, offsetToCoords(logical_row_start_offset + i,
${textureWidth}, ${textureHeight}))) - max);
}
return norm_factor;
}`;
return Object.assign(Object.assign({}, softmaxComputeScaleProgramMetadata), { output: { dims: outputShape, type: input.type, textureType: types_1.TextureType.unpacked }, shaderSource });
};
const createSoftMaxProgramInfo = (inferenceHandler, input, logicalRowCount, featureCount, maxElementPerLogicalRow, normalizationPerLogicalRow) => {
const [textureWidth, textureHeight] = inferenceHandler.calculateTextureWidthAndHeight(input.dims, types_1.TextureType.unpacked);
const rank = input.dims.length;
if (logicalRowCount < 1 || featureCount < 1) {
throw new Error('Logical row count N and feature count D must be greater than or equal to 1');
}
if (maxElementPerLogicalRow.length !== 1 || normalizationPerLogicalRow.length !== 1) {
throw new Error('Dimensionality of the intermediate results should be 1');
}
if (maxElementPerLogicalRow[0] !== logicalRowCount || normalizationPerLogicalRow[0] !== logicalRowCount) {
throw new Error('Shape of the intermediate results should be equal to logical row count');
}
const shaderSource = `
float process(int[${rank}] indices) {
// get offset of current logical tensor index from the 2-D texture coordinates (TexCoords)
int offset = coordsToOffset(TexCoords, ${textureWidth}, ${textureHeight});
//determine the logical row for this index
int logical_row_index[1];
logical_row_index[0] = offset / ${featureCount};
float norm_factor = _Norm(logical_row_index);
// avoid possible division by 0
// if norm_facor is 0, all elements are zero
// if so, return 0
if(norm_factor == 0.0)
return 0.0;
return exp(_A(indices) - _Max(logical_row_index)) / norm_factor;
}`;
return Object.assign(Object.assign({}, softmaxProgramMetadata), { output: { dims: input.dims, type: input.type, textureType: types_1.TextureType.unpacked }, shaderSource });
};
const validateInputs = (inputs) => {
if (!inputs || inputs.length !== 1) {
throw new Error('Softmax requires 1 input.');
}
if (inputs[0].type !== 'float32' && inputs[0].type !== 'float64') {
throw new Error('Invalid input type');
}
};
//# sourceMappingURL=softmax.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,282 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import {AttributeWithCacheKey, createAttributeWithCacheKey} from '../../../attribute-with-cache-key';
import {Graph} from '../../../graph';
import {OperatorImplementation, OperatorInitialization} from '../../../operators';
import {Tensor} from '../../../tensor';
import {ShapeUtil} from '../../../util';
import {getGlsl} from '../glsl-source';
import {WebGLInferenceHandler} from '../inference-handler';
import {ProgramInfo, TextureType} from '../types';
import {transpose, TransposeAttributes} from './transpose';
export interface SoftmaxAttributes extends AttributeWithCacheKey {
readonly axis: number;
}
const softmaxComputeMaxProgramMetadata = {
name: 'SoftmaxComputeMax',
inputNames: ['A'],
inputTypes: [TextureType.unpacked],
};
const softmaxComputeScaleProgramMetadata = {
name: 'SoftmaxComputeScale',
inputNames: ['A', 'Max'],
inputTypes: [TextureType.unpacked, TextureType.unpacked],
};
const softmaxProgramMetadata = {
name: 'SoftMax',
inputNames: ['A', 'Max', 'Norm'],
inputTypes: [TextureType.unpacked, TextureType.unpacked, TextureType.unpacked],
};
export const softmax: OperatorImplementation<SoftmaxAttributes> =
(inferenceHandler: WebGLInferenceHandler, inputs: Tensor[], attributes: SoftmaxAttributes): Tensor[] => {
validateInputs(inputs);
const inputShape = inputs[0].dims.slice();
const axis = ShapeUtil.normalizeAxis(attributes.axis, inputShape.length);
const logicalRowCount = ShapeUtil.sizeToDimension(inputShape, axis);
const featureCount = ShapeUtil.sizeFromDimension(inputShape, axis);
const output = computeSoftmax(inferenceHandler, inputs, attributes, logicalRowCount, featureCount);
return output;
};
export const parseSoftmaxAttributes: OperatorInitialization<SoftmaxAttributes> =
(node: Graph.Node): SoftmaxAttributes => createAttributeWithCacheKey({axis: node.attributes.getInt('axis', 1)});
export const parseSoftmaxAttributesV13: OperatorInitialization<SoftmaxAttributes> =
(node: Graph.Node): SoftmaxAttributes => createAttributeWithCacheKey({axis: node.attributes.getInt('axis', -1)});
// The "semantic" meaning of axis has changed in opset-13.
// Please compare: https://github.com/onnx/onnx/blob/main/docs/Operators.md#Softmax
// with https://github.com/onnx/onnx/blob/main/docs/Changelog.md#Softmax-11 for detailed explanations
// To account for the opset-13 behavior, our plan will be to transpose the "axis" dim to the innermost dim
// and perform softmax and then reverse the transpose. We can skip the transposing aspect if the axis is already
// the innermost dim
export const softmaxV13: OperatorImplementation<SoftmaxAttributes> =
(inferenceHandler: WebGLInferenceHandler, inputs: Tensor[], attributes: SoftmaxAttributes): Tensor[] => {
validateInputs(inputs);
const inputShape = inputs[0].dims.slice();
const axis = ShapeUtil.normalizeAxis(attributes.axis, inputShape.length);
const rank = inputShape.length;
const isTransposeRequired = (axis !== rank - 1) ? true : false;
const transposedInputShape: number[] = [];
let perm: number[] = [];
let transposedInputs: Tensor[] = [];
let transposeAttribute: TransposeAttributes;
if (isTransposeRequired) {
perm = Array.from({length: rank}).map((_, i) => i);
// swap the innermost dim with the dim corresponding to axis
perm[axis] = rank - 1;
perm[rank - 1] = axis;
perm.map(p => transposedInputShape.push(inputShape[p]));
transposeAttribute = createAttributeWithCacheKey({perm});
transposedInputs = transpose(inferenceHandler, inputs, transposeAttribute);
}
const logicalRowCount = isTransposeRequired ? ShapeUtil.sizeToDimension(transposedInputShape, rank - 1) :
ShapeUtil.sizeToDimension(inputShape, rank - 1);
const featureCount = isTransposeRequired ? ShapeUtil.sizeFromDimension(transposedInputShape, rank - 1) :
ShapeUtil.sizeFromDimension(inputShape, rank - 1);
const output = computeSoftmax(
inferenceHandler, isTransposeRequired ? transposedInputs : inputs, attributes, logicalRowCount, featureCount);
if (isTransposeRequired) {
const reversedOutput = transpose(inferenceHandler, output, transposeAttribute!);
return reversedOutput;
} else {
return output;
}
};
const computeSoftmax =
(inferenceHandler: WebGLInferenceHandler, inputs: Tensor[], attributes: SoftmaxAttributes, logicalRowCount: number,
featureCount: number): Tensor[] => {
const computeMaxProgramInfo =
createComputeMaxProgramInfo(inferenceHandler, inputs[0], logicalRowCount, featureCount, [logicalRowCount]);
const max = inferenceHandler.run(
{...softmaxComputeMaxProgramMetadata, cacheHint: attributes.cacheKey, get: () => computeMaxProgramInfo},
inputs);
const computeScaleProgramInfo = createComputScaleProgramInfo(
inferenceHandler, inputs[0], logicalRowCount, featureCount, computeMaxProgramInfo.output.dims,
[logicalRowCount]);
const scale = inferenceHandler.run(
{...softmaxComputeScaleProgramMetadata, cacheHint: attributes.cacheKey, get: () => computeScaleProgramInfo},
[inputs[0], max]);
const softMaxProgramInfo = createSoftMaxProgramInfo(
inferenceHandler, inputs[0], logicalRowCount, featureCount, computeMaxProgramInfo.output.dims,
computeScaleProgramInfo.output.dims);
const output = inferenceHandler.run(
{...softmaxProgramMetadata, cacheHint: attributes.cacheKey, get: () => softMaxProgramInfo},
[inputs[0], max, scale]);
return [output];
};
/**
* Create a texture that contains the maximum value of each of the 'N' rows
*/
const createComputeMaxProgramInfo =
(inferenceHandler: WebGLInferenceHandler, input: Tensor, logicalRowCount: number, featureCount: number,
outputShape: number[]): ProgramInfo => {
const [textureWidth, textureHeight] =
inferenceHandler.calculateTextureWidthAndHeight(input.dims, TextureType.unpacked);
const rank = outputShape.length;
if (logicalRowCount < 1 || featureCount < 1) {
throw new Error('Logical row count N and feature count D must be greater than or equal to 1');
}
if (outputShape.length !== 1) {
throw new Error('Dimensionality of the output should be 1');
}
if (outputShape[0] !== logicalRowCount) {
throw new Error('Shape of the output should be equal to logical row count');
}
const glsl = getGlsl(inferenceHandler.session.backend.glContext.version);
const shaderSource = `
float process(int[${rank}] indices) {
int logical_row_start_offset = indices[0] * ${featureCount};
float max = getColorAsFloat(${glsl.texture2D}(A, offsetToCoords(logical_row_start_offset, ${textureWidth},
${textureHeight} )));
for(int i=1; i<${featureCount}; ++i)
{
float current = getColorAsFloat(${glsl.texture2D}(A, offsetToCoords(logical_row_start_offset + i,
${textureWidth}, ${textureHeight})));
if(current > max)
max = current;
}
return max;
}`;
return {
...softmaxComputeMaxProgramMetadata,
output: {dims: outputShape, type: input.type, textureType: TextureType.unpacked},
shaderSource
};
};
/**
* Create a texture that contains the normalization factor for each of the 'N' rows
*/
const createComputScaleProgramInfo =
(inferenceHandler: WebGLInferenceHandler, input: Tensor, logicalRowCount: number, featureCount: number,
maxElementPerLogicalRow: readonly number[], outputShape: number[]): ProgramInfo => {
const [textureWidth, textureHeight] =
inferenceHandler.calculateTextureWidthAndHeight(input.dims, TextureType.unpacked);
const rank = outputShape.length;
if (logicalRowCount < 1 || featureCount < 1) {
throw new Error('Logical row count N and feature count D must be greater than or equal to 1');
}
if (outputShape.length !== 1) {
throw new Error('Dimensionality of the output should be 1');
}
if (outputShape[0] !== logicalRowCount) {
throw new Error('Shape of the output should be equal to logical row count');
}
if (maxElementPerLogicalRow.length !== 1) {
throw new Error('Dimensionality of the intermediate results should be 1');
}
if (maxElementPerLogicalRow[0] !== logicalRowCount) {
throw new Error('Shape of the intermediate results should be equal to logical row count');
}
const glsl = getGlsl(inferenceHandler.session.backend.glContext.version);
const shaderSource = `
float process(int[${rank}] indices) {
int logical_row_start_offset = indices[0] * ${featureCount};
float norm_factor = 0.0;
float max = _Max(indices);
for(int i=0; i<${featureCount}; ++i)
{
norm_factor += exp(getColorAsFloat(${glsl.texture2D}(A, offsetToCoords(logical_row_start_offset + i,
${textureWidth}, ${textureHeight}))) - max);
}
return norm_factor;
}`;
return {
...softmaxComputeScaleProgramMetadata,
output: {dims: outputShape, type: input.type, textureType: TextureType.unpacked},
shaderSource
};
};
const createSoftMaxProgramInfo =
(inferenceHandler: WebGLInferenceHandler, input: Tensor, logicalRowCount: number, featureCount: number,
maxElementPerLogicalRow: readonly number[], normalizationPerLogicalRow: readonly number[]): ProgramInfo => {
const [textureWidth, textureHeight] =
inferenceHandler.calculateTextureWidthAndHeight(input.dims, TextureType.unpacked);
const rank = input.dims.length;
if (logicalRowCount < 1 || featureCount < 1) {
throw new Error('Logical row count N and feature count D must be greater than or equal to 1');
}
if (maxElementPerLogicalRow.length !== 1 || normalizationPerLogicalRow.length !== 1) {
throw new Error('Dimensionality of the intermediate results should be 1');
}
if (maxElementPerLogicalRow[0] !== logicalRowCount || normalizationPerLogicalRow[0] !== logicalRowCount) {
throw new Error('Shape of the intermediate results should be equal to logical row count');
}
const shaderSource = `
float process(int[${rank}] indices) {
// get offset of current logical tensor index from the 2-D texture coordinates (TexCoords)
int offset = coordsToOffset(TexCoords, ${textureWidth}, ${textureHeight});
//determine the logical row for this index
int logical_row_index[1];
logical_row_index[0] = offset / ${featureCount};
float norm_factor = _Norm(logical_row_index);
// avoid possible division by 0
// if norm_facor is 0, all elements are zero
// if so, return 0
if(norm_factor == 0.0)
return 0.0;
return exp(_A(indices) - _Max(logical_row_index)) / norm_factor;
}`;
return {
...softmaxProgramMetadata,
output: {dims: input.dims, type: input.type, textureType: TextureType.unpacked},
shaderSource
};
};
const validateInputs = (inputs: Tensor[]): void => {
if (!inputs || inputs.length !== 1) {
throw new Error('Softmax requires 1 input.');
}
if (inputs[0].type !== 'float32' && inputs[0].type !== 'float64') {
throw new Error('Invalid input type');
}
};

View File

@@ -0,0 +1,59 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseSplitAttributes = exports.split = void 0;
const attribute_with_cache_key_1 = require("../../../attribute-with-cache-key");
const util_1 = require("../../../util");
const types_1 = require("../types");
const splitProgramMetadata = {
name: 'Split',
inputNames: ['A'],
inputTypes: [types_1.TextureType.unpacked],
};
const split = (inferenceHandler, inputs, attributes) => {
validateInputs(inputs);
const axis = util_1.ShapeUtil.normalizeAxis(attributes.axis, inputs[0].dims.length);
const count = getProgramCount(inferenceHandler, inputs, axis, attributes);
const output = [];
for (let i = 0; i < count; ++i) {
output.push(inferenceHandler.run(Object.assign(Object.assign({}, splitProgramMetadata), { cacheHint: `${attributes.cacheKey};${i}`, get: () => createSplitProgramInfo(inferenceHandler, inputs[0], attributes, axis, i) }), inputs));
}
return output;
};
exports.split = split;
const parseSplitAttributes = (node) => {
const axis = node.attributes.getInt('axis', 0);
const split = node.attributes.getInts('split', []);
const numOutputs = node.outputs.length;
return (0, attribute_with_cache_key_1.createAttributeWithCacheKey)({ axis, split, numOutputs });
};
exports.parseSplitAttributes = parseSplitAttributes;
const getProgramCount = (inferenceHandler, inputs, axis, attributes) => {
const [, offsets] = util_1.SplitUtil.splitShape(inputs[0].dims, axis, attributes.split, attributes.numOutputs);
return offsets.length;
};
const createSplitProgramInfo = (inferenceHandler, input, attributes, axis, index) => {
const [shapes, offsets] = util_1.SplitUtil.splitShape(input.dims, axis, attributes.split, attributes.numOutputs);
const offset = offsets[index];
const outputShape = shapes[index];
const rank = outputShape.length;
const shaderSource = `
float process(int indices[${rank}]) {
indices[${axis}] += ${offset};
return _A(indices);
}
`;
return Object.assign(Object.assign({}, splitProgramMetadata), { cacheHint: `${attributes.cacheKey}:${index}`, output: { dims: outputShape, type: input.type, textureType: types_1.TextureType.unpacked }, shaderSource });
};
const validateInputs = (inputs) => {
if (!inputs || inputs.length !== 1) {
throw new Error('Split requires one input.');
}
if (inputs[0].type !== 'int8' && inputs[0].type !== 'uint8' && inputs[0].type !== 'int16' &&
inputs[0].type !== 'uint16' && inputs[0].type !== 'int32' && inputs[0].type !== 'uint32' &&
inputs[0].type !== 'float32' && inputs[0].type !== 'float64' && inputs[0].type !== 'bool') {
throw new Error('Invalid input type.');
}
};
//# sourceMappingURL=split.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"split.js","sourceRoot":"","sources":["split.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,88 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import {AttributeWithCacheKey, createAttributeWithCacheKey} from '../../../attribute-with-cache-key';
import {Graph} from '../../../graph';
import {OperatorImplementation, OperatorInitialization} from '../../../operators';
import {Tensor} from '../../../tensor';
import {ShapeUtil, SplitUtil} from '../../../util';
import {WebGLInferenceHandler} from '../inference-handler';
import {ProgramInfo, TextureType} from '../types';
export interface SplitAttributes extends AttributeWithCacheKey {
readonly axis: number;
readonly split: number[];
readonly numOutputs: number;
}
const splitProgramMetadata = {
name: 'Split',
inputNames: ['A'],
inputTypes: [TextureType.unpacked],
};
export const split: OperatorImplementation<SplitAttributes> =
(inferenceHandler: WebGLInferenceHandler, inputs: Tensor[], attributes: SplitAttributes): Tensor[] => {
validateInputs(inputs);
const axis = ShapeUtil.normalizeAxis(attributes.axis, inputs[0].dims.length);
const count = getProgramCount(inferenceHandler, inputs, axis, attributes);
const output: Tensor[] = [];
for (let i = 0; i < count; ++i) {
output.push(inferenceHandler.run(
{
...splitProgramMetadata,
cacheHint: `${attributes.cacheKey};${i}`,
get: () => createSplitProgramInfo(inferenceHandler, inputs[0], attributes, axis, i)
},
inputs));
}
return output;
};
export const parseSplitAttributes: OperatorInitialization<SplitAttributes> = (node: Graph.Node): SplitAttributes => {
const axis = node.attributes.getInt('axis', 0);
const split = node.attributes.getInts('split', []);
const numOutputs = node.outputs.length;
return createAttributeWithCacheKey({axis, split, numOutputs});
};
const getProgramCount =
(inferenceHandler: WebGLInferenceHandler, inputs: Tensor[], axis: number, attributes: SplitAttributes): number => {
const [, offsets] = SplitUtil.splitShape(inputs[0].dims, axis, attributes.split, attributes.numOutputs);
return offsets.length;
};
const createSplitProgramInfo =
(inferenceHandler: WebGLInferenceHandler, input: Tensor, attributes: SplitAttributes, axis: number, index: number):
ProgramInfo => {
const [shapes, offsets] = SplitUtil.splitShape(input.dims, axis, attributes.split, attributes.numOutputs);
const offset = offsets[index];
const outputShape = shapes[index];
const rank = outputShape.length;
const shaderSource = `
float process(int indices[${rank}]) {
indices[${axis}] += ${offset};
return _A(indices);
}
`;
return {
...splitProgramMetadata,
cacheHint: `${attributes.cacheKey}:${index}`,
output: {dims: outputShape, type: input.type, textureType: TextureType.unpacked},
shaderSource
};
};
const validateInputs = (inputs: Tensor[]): void => {
if (!inputs || inputs.length !== 1) {
throw new Error('Split requires one input.');
}
if (inputs[0].type !== 'int8' && inputs[0].type !== 'uint8' && inputs[0].type !== 'int16' &&
inputs[0].type !== 'uint16' && inputs[0].type !== 'int32' && inputs[0].type !== 'uint32' &&
inputs[0].type !== 'float32' && inputs[0].type !== 'float64' && inputs[0].type !== 'bool') {
throw new Error('Invalid input type.');
}
};

View File

@@ -0,0 +1,37 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseSqueezeAttributes = exports.squeezeV13 = exports.squeeze = void 0;
const util_1 = require("../../../util");
const squeeze = (inferenceHandler, inputs, axes) => {
validateInputs(inputs);
const outputShape = util_1.ShapeUtil.squeezeShape(inputs[0].dims, axes);
const output = inferenceHandler.reshapeUnpacked(inputs[0], outputShape);
return [output];
};
exports.squeeze = squeeze;
const squeezeV13 = (inferenceHandler, inputs) => {
validateInputsV13(inputs);
return (0, exports.squeeze)(inferenceHandler, [inputs[0]], Array.from(inputs[1].integerData));
};
exports.squeezeV13 = squeezeV13;
const parseSqueezeAttributes = (node) => node.attributes.getInts('axes');
exports.parseSqueezeAttributes = parseSqueezeAttributes;
const validateInputs = (inputs) => {
if (!inputs || inputs.length !== 1) {
throw new Error('Squeeze requires 1 input.');
}
if (inputs[0].type === 'string') {
throw new Error('invalid input tensor types.');
}
};
const validateInputsV13 = (inputs) => {
if (!inputs || inputs.length !== 2) {
throw new Error('Squeeze requires 2 inputs.');
}
if (inputs[1].type !== 'int32') {
throw new Error('Invalid input type.');
}
};
//# sourceMappingURL=squeeze.js.map

Some files were not shown because too many files have changed in this diff Show More