| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387 |
- const tvm = {};
- tvm.ModelFactory = class {
- async match(context) {
- const identifier = context.identifier;
- const extension = identifier.lastIndexOf('.') > 0 ? identifier.split('.').pop().toLowerCase() : '';
- if (extension === 'json') {
- const obj = await context.peek('json');
- if (obj && Array.isArray(obj.nodes) && Array.isArray(obj.arg_nodes) && Array.isArray(obj.heads) &&
- obj.nodes.every((node) => node && (node.op === 'null' || node.op === 'tvm_op'))) {
- return context.set('tvm.json', obj);
- }
- }
- const stream = context.stream;
- const signature = [0xB7, 0x9C, 0x04, 0x05, 0x4F, 0x8D, 0xE5, 0xF7];
- if (stream && signature.length <= stream.length && stream.peek(signature.length).every((value, index) => value === signature[index])) {
- return context.set('tvm.params');
- }
- return null;
- }
- filter(context, match) {
- return context.type !== 'tvm.json' || match.type !== 'tvm.params';
- }
- async open(context) {
- const metadata = await context.metadata('tvm-metadata.json');
- let obj = null;
- let params = null;
- switch (context.type) {
- case 'tvm.json': {
- obj = context.value;
- const identifier = context.identifier.replace(/\.json$/, '.params');
- try {
- const content = await context.fetch(identifier);
- const reader = await content.read('binary');
- params = tvm.NDArray.loadParams(reader);
- } catch {
- // continue regardless of error
- }
- break;
- }
- case 'tvm.params': {
- const identifier = context.identifier.replace(/\.params$/, '.json');
- try {
- const content = await context.fetch(identifier);
- obj = await content.read('json');
- } catch {
- // continue regardless of error
- }
- const reader = await context.read('binary');
- params = tvm.NDArray.loadParams(reader);
- break;
- }
- default:
- throw new tvm.Error(`Unsupported TVN format '${context.type}'.`);
- }
- return new tvm.Model(metadata, obj, params);
- }
- };
- tvm.Model = class {
- constructor(metadata, obj, params) {
- this.format = 'TVM';
- this.modules = [new tvm.Graph(metadata, obj, params)];
- }
- };
- tvm.Graph = class {
- constructor(metadata, obj, params) {
- this.nodes = [];
- this.inputs = [];
- this.outputs = [];
- const tensors = new Map();
- if (params) {
- for (const [name, value] of params) {
- const shape = new tvm.TensorShape(value.shape);
- const type = new tvm.TensorType(value.dtype, shape);
- const tensor = new tvm.Tensor(name, type, value.data);
- tensors.set(name, tensor);
- }
- }
- const values = new Map();
- values.map = (name, type, tensor) => {
- if (!values.has(name)) {
- values.set(name, new tvm.Value(name, type || null, tensor || null));
- } else if (type || (tensor && tensor !== values.get(name).initializer)) {
- throw new tvm.Error(`Duplicate value '${name}'.`);
- }
- return values.get(name);
- };
- const updateOutput = (nodes, input) => {
- const [nodeIndex, outputIndex] = input;
- const node = nodes[nodeIndex];
- if (node) {
- while (outputIndex >= node.outputs.length) {
- node.outputs.push([nodeIndex, node.outputs.length]);
- }
- }
- return [nodeIndex, outputIndex];
- };
- if (obj) {
- const nodes = obj.nodes;
- const inputs = {};
- const outputs = {};
- for (const node of nodes) {
- node.outputs = [];
- }
- for (const node of nodes) {
- node.inputs = node.inputs || [];
- node.inputs = node.inputs.map((input) => updateOutput(nodes, input));
- }
- const arg_nodes = new Map(obj.arg_nodes.map((index) => [index, index < nodes.length ? nodes[index] : null]));
- for (let i = 0; i < obj.heads.length; i++) {
- const head = obj.heads[i];
- const identifier = updateOutput(nodes, head);
- const name = `output${(i === 0) ? '' : (i + 1)}`;
- const signature = outputs[name];
- const type = signature && signature.data_shape ? new tvm.TensorType(-1, new tvm.TensorShape(signature.data_shape)) : null;
- const value = values.map(`[${identifier.join(',')}]`, type);
- const argument = new tvm.Argument(name, [value]);
- this.outputs.push(argument);
- }
- const filtered = nodes.filter((node, index) => !arg_nodes.has(index));
- const initializers = new Map();
- for (const node of filtered) {
- for (const input of node.inputs) {
- const identifier = `[${input.join(',')}]`;
- if (!initializers.has(identifier)) {
- const [index] = input;
- const arg_node = arg_nodes.get(index);
- if (arg_node && arg_node.name && (!arg_node.inputs || arg_node.inputs.length === 0) && (arg_node.outputs && arg_node.outputs.length === 1)) {
- if (tensors.has(arg_node.name)) {
- initializers.set(identifier, tensors.get(arg_node.name));
- arg_nodes.delete(index);
- }
- }
- }
- }
- if (node.params) {
- for (const param of node.params) {
- values.map(param.id, null, tensors.get(param.id));
- }
- }
- }
- for (const [, arg_node] of arg_nodes) {
- if (arg_node && (!arg_node.inputs || arg_node.inputs.length === 0) && (arg_node.outputs && arg_node.outputs.length === 1)) {
- const identifier = `[${arg_node.outputs[0].join(',')}]`;
- const name = arg_node.name;
- const signature = inputs[name];
- const type = signature && signature.data_shape ? new tvm.TensorType(-1, new tvm.TensorShape(signature.data_shape)) : null;
- const value = values.map(identifier, type, tensors.get(identifier));
- const argument = new tvm.Argument(name, [value]);
- this.inputs.push(argument);
- }
- }
- for (const node of filtered) {
- this.nodes.push(new tvm.Node(metadata, node, initializers, values));
- }
- } else if (params) {
- const blocks = new Map();
- const separator = Array.from(params.keys()).every((key) => key.indexOf('_') !== -1) ? '_' : '';
- for (const [key] of params) {
- const parts = separator ? key.split(separator) : [key];
- let argumentName = parts.pop();
- if (key.endsWith('moving_mean') || key.endsWith('moving_var')) {
- argumentName = [parts.pop(), argumentName].join(separator);
- }
- const nodeName = parts.join(separator);
- if (!blocks.has(nodeName)) {
- blocks.set(nodeName, { name: nodeName, op: 'Weights', params: [] });
- }
- blocks.get(nodeName).params.push({ name: argumentName, id: key });
- values.map(key, null, tensors.get(key));
- }
- for (const block of blocks.values()) {
- const node = new tvm.Node(metadata, block, new Map(), values);
- this.nodes.push(node);
- }
- }
- }
- };
- tvm.Argument = class {
- constructor(name, value, type = null, visible = true) {
- this.name = name;
- this.value = value;
- this.type = type;
- this.visible = visible;
- }
- };
- tvm.Value = class {
- constructor(name, type, initializer = null) {
- if (typeof name !== 'string') {
- throw new tvm.Error(`Invalid value identifier '${JSON.stringify(name)}'.`);
- }
- this.name = !name && initializer && initializer.name ? initializer.name : name;
- this.type = !type && initializer && initializer.type ? initializer.type : type;
- this.initializer = initializer;
- }
- };
- tvm.Node = class {
- constructor(metadata, node, initializers, values) {
- this.type = { name: node.op };
- this.name = node.name;
- this.attributes = Object.entries(node.attrs || {}).map(([name, value]) => new tvm.Argument(name, value));
- this.inputs = (node.inputs || []).map((input, index) => {
- const name = index.toString();
- const identifier = `[${input.join(',')}]`;
- const value = values.map(identifier, null, initializers.get(identifier));
- return new tvm.Argument(name, [value]);
- });
- this.outputs = (node.outputs || []).map((output, index) => {
- const name = index.toString();
- const value = values.map(`[${output.join(',')}]`);
- return new tvm.Argument(name, [value]);
- });
- for (const param of node.params || []) {
- const value = values.map(param.id);
- const argument = new tvm.Argument(param.name, [value]);
- this.inputs.push(argument);
- }
- }
- };
- tvm.Tensor = class {
- constructor(name, type, data) {
- this.name = name;
- this.type = type;
- this.values = data;
- this.encoding = '<';
- }
- };
- tvm.TensorType = class {
- constructor(dtype, shape) {
- let type = '';
- switch (dtype.code) { // TVMArgTypeCode
- case 0: type = 'int'; break;
- case 1: type = 'uint'; break;
- case 2: type = 'float'; break;
- default: throw new tvm.Error(`Unsupported data type code '${dtype.code}'.`);
- }
- if (dtype.lanes !== 1) {
- throw new tvm.Error(`Unsupported data type lanes '${dtype.lanes}'.`);
- }
- this.dataType = `${type}${dtype.bits}`;
- this.shape = shape;
- }
- toString() {
- return this.dataType + this.shape.toString();
- }
- };
- tvm.TensorShape = class {
- constructor(dimensions) {
- this.dimensions = Array.isArray(dimensions) ? dimensions.map((dim) => typeof dim === 'bigint' ? dim.toNumber() : dim) : dimensions;
- }
- toString() {
- if (this.dimensions) {
- if (this.dimensions.length === 0) {
- return '';
- }
- return `[${this.dimensions.map((dimension) => dimension.toString()).join(',')}]`;
- }
- return '';
- }
- };
- tvm.NDArray = class {
- static loadParams(reader) {
- // https://github.com/apache/tvm/blob/main/src/runtime/file_utils.cc
- reader = new tvm.BinaryReader(reader);
- const header = reader.read(8);
- const signature = [0xB7, 0x9C, 0x04, 0x05, 0x4F, 0x8D, 0xE5, 0xF7];
- if (!header.every((value, index) => value === signature[index])) {
- throw new tvm.Error('Invalid signature.');
- }
- reader.skip(8); // reserved
- const names = reader.strings();
- const values = new Array(reader.uint64().toNumber());
- if (names.length !== values.length) {
- throw new tvm.Error('Invalid parameters.');
- }
- const params = new Map();
- for (let i = 0; i < values.length; i++) {
- const value = new tvm.NDArray(reader);
- params.set(names[i], value);
- }
- return params;
- }
- constructor(reader) {
- // https://github.com/apache/tvm/blob/main/include/tvm/runtime/ndarray.h
- const header = reader.read(8);
- const signature = [0x3F, 0xA1, 0xB4, 0x96, 0xF0, 0x40, 0x5E, 0xDD];
- if (!header.every((value, index) => value === signature[index])) {
- throw new tvm.Error('Invalid signature.');
- }
- reader.skip(8); // reserved
- this.device = {
- deviceType: reader.uint32(),
- deviceId: reader.uint32()
- };
- this.shape = new Array(reader.uint32());
- this.dtype = {
- code: reader.uint8(),
- bits: reader.uint8(),
- lanes: reader.uint16(),
- };
- for (let i = 0; i < this.shape.length; i++) {
- this.shape[i] = reader.uint64();
- }
- const size = reader.uint64().toNumber();
- this.data = reader.read(size);
- }
- };
- tvm.BinaryReader = class {
- constructor(reader) {
- this._reader = reader;
- }
- skip(offset) {
- this._reader.skip(offset);
- }
- read(length) {
- return this._reader.read(length);
- }
- uint8() {
- return this._reader.byte();
- }
- uint16() {
- return this._reader.uint16();
- }
- uint32() {
- return this._reader.uint32();
- }
- uint64() {
- return this._reader.uint64();
- }
- string() {
- const length = this.uint64().toNumber();
- const buffer = this._reader.read(length);
- return String.fromCharCode.apply(null, new Uint8Array(buffer));
- }
- strings() {
- const list = new Array(this.uint64().toNumber());
- for (let i = 0; i < list.length; i++) {
- list[i] = this.string();
- }
- return list;
- }
- };
- tvm.Error = class extends Error {
- constructor(message) {
- super(message);
- this.name = 'Error loading TVM model.';
- }
- };
- export const ModelFactory = tvm.ModelFactory;
|