executorch.js 48 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338
  1. // Experimental
  2. const executorch = {};
  3. const coreml = {};
  4. const vulkan = {};
  5. const xnnpack = {};
  6. const qnn = {};
  7. const ethosu = {};
  8. const openvino = {};
  9. const rockchip = {};
  10. import * as base from './base.js';
  11. import * as python from './python.js';
  12. import * as pytorch from './pytorch.js';
  13. executorch.ModelFactory = class {
  14. async match(context) {
  15. const reader = await executorch.Reader.open(context);
  16. if (reader) {
  17. return context.set('executorch', reader);
  18. }
  19. return null;
  20. }
  21. async open(context) {
  22. executorch.schema = await context.require('./executorch-schema');
  23. const target = context.value;
  24. await target.read();
  25. return new executorch.Model(target);
  26. }
  27. };
  28. executorch.Model = class {
  29. constructor(target) {
  30. this.format = `ExecuTorch v${target.program.version}`;
  31. this.modules = [];
  32. for (const plan of target.program.execution_plan) {
  33. for (const chain of plan.chains) {
  34. const graph = new executorch.Graph(target, plan, chain);
  35. this.modules.push(graph);
  36. }
  37. }
  38. }
  39. };
  40. executorch.Graph = class {
  41. constructor(target, plan, chain) {
  42. this.name = plan.name || '';
  43. this.inputs = [];
  44. this.outputs = [];
  45. this.nodes = [];
  46. const values = new Map();
  47. values.tensors = (index, items) => {
  48. const list = [];
  49. for (let i = 0; i < items.length; i++) {
  50. const item = items[i];
  51. const type = item ? new executorch.TensorType(item) : null;
  52. let initializer = null;
  53. if (item && item.data_buffer_idx > 0) {
  54. initializer = new executorch.Tensor(item, target);
  55. }
  56. const identifier = items.length > 1 ? `${index}.${i}` : index.toString();
  57. const value = new executorch.Value(identifier, type, initializer);
  58. list.push(value);
  59. }
  60. return list;
  61. };
  62. values.map = (index, output) => {
  63. if (!values.has(index)) {
  64. const executorch_flatbuffer = executorch.schema.executorch_flatbuffer;
  65. const val = plan.values[index].val;
  66. const tensor = val instanceof executorch_flatbuffer.Tensor || val instanceof executorch_flatbuffer.TensorList || val instanceof executorch_flatbuffer.OptionalTensorList;
  67. if (output && !tensor) {
  68. const value = [new executorch.Value(index.toString(), null, null)];
  69. values.set(index, { type: null, value });
  70. } else if (val instanceof executorch_flatbuffer.Null) {
  71. values.set(index, { type: 'attribute', value: null });
  72. } else if (val instanceof executorch_flatbuffer.Int) {
  73. values.set(index, { type: 'int64', value: val.int_val });
  74. } else if (val instanceof executorch_flatbuffer.Bool) {
  75. values.set(index, { type: 'int64', value: val.bool_val });
  76. } else if (val instanceof executorch_flatbuffer.Double) {
  77. values.set(index, { type: 'float64', value: val.double_val });
  78. } else if (val instanceof executorch_flatbuffer.Tensor) {
  79. const items = [val];
  80. values.set(index, { type: null, value: values.tensors(index, items) });
  81. } else if (val instanceof executorch_flatbuffer.String) {
  82. values.set(index, { type: 'string', value: val.string_val });
  83. } else if (val instanceof executorch_flatbuffer.IntList) {
  84. const list = val.items.map((index) => plan.values[index].val.int_val);
  85. values.set(index, { type: 'int64[]', value: list });
  86. } else if (val instanceof executorch_flatbuffer.DoubleList) {
  87. values.set(index, { type: 'float64[]', value: Array.from(val.items) });
  88. } else if (val instanceof executorch_flatbuffer.BoolList) {
  89. throw new executorch.Error('executorch_flatbuffer.BoolList not implemented.');
  90. } else if (val instanceof executorch_flatbuffer.TensorList) {
  91. const items = Array.from(val.items).map((arg) => arg === -1 ? null : plan.values[arg].val);
  92. values.set(index, { type: null, value: values.tensors(index, items) });
  93. } else if (val instanceof executorch_flatbuffer.OptionalTensorList) {
  94. const items = Array.from(val.items).map((arg) => arg === -1 ? null : plan.values[arg].val);
  95. values.set(index, { type: null, value: values.tensors(index, items) });
  96. } else {
  97. throw new Error(`Value type '${val.constructor.name}' not implemented.`);
  98. }
  99. }
  100. return values.get(index);
  101. };
  102. for (let i = 0; i < plan.inputs.length; i++) {
  103. const input = plan.inputs[i];
  104. const value = values.map(input);
  105. const name = plan.inputs.length === 1 ? 'input' : `input.${i}`;
  106. const argument = new executorch.Argument(name, value.value, value.type);
  107. this.inputs.push(argument);
  108. }
  109. for (let i = 0; i < plan.outputs.length; i++) {
  110. const output = plan.outputs[i];
  111. const value = values.map(output);
  112. const name = plan.outputs.length === 1 ? 'output' : `output.${i}`;
  113. const argument = new executorch.Argument(name, value.value, value.type);
  114. this.outputs.push(argument);
  115. }
  116. for (const instruction of chain.instructions) {
  117. const node = new executorch.Node(target, plan, chain, instruction, values);
  118. this.nodes.push(node);
  119. }
  120. }
  121. };
  122. executorch.Argument = class {
  123. constructor(name, value, type = null, visible = true) {
  124. this.name = name;
  125. this.value = value;
  126. this.type = type;
  127. this.visible = visible;
  128. }
  129. };
  130. executorch.Value = class Value {
  131. constructor(name, type, initializer = null) {
  132. if (typeof name !== 'string') {
  133. throw new executorch.Error(`Invalid value identifier '${JSON.stringify(name)}'.`);
  134. }
  135. this.name = name;
  136. this.type = initializer && initializer.type ? initializer.type : type || null;
  137. this.initializer = initializer;
  138. }
  139. };
  140. executorch.Node = class {
  141. constructor(target, plan, chain, instruction, values) {
  142. this.name = '';
  143. this.inputs = [];
  144. this.outputs = [];
  145. this.attributes = [];
  146. const instr_args = instruction.instr_args;
  147. const executorch_flatbuffer = executorch.schema.executorch_flatbuffer;
  148. if (instr_args instanceof executorch_flatbuffer.KernelCall) {
  149. const op = plan.operators[instr_args.op_index];
  150. const name = op.name.split('::').pop();
  151. const identifier = op.overload ? `${op.name}.${op.overload}` : op.name;
  152. const torch = target.execution.__import__('torch');
  153. const schemas = torch._C._jit_get_schemas_for_operator(op.name);
  154. const schema = schemas.find((schema) => schema.name === op.name && schema.overload_name === op.overload);
  155. if (!schema) {
  156. throw new executorch.Error(`Operator schema for '${identifier}' not found.`);
  157. }
  158. const category = schema && schema.category ? schema.category : '';
  159. const alias = (arg) => arg && arg.alias_info && arg.alias_info.before_set.length === 1 ? arg.alias_info.before_set[0] : null;
  160. const outputs = new Set(schema && Array.isArray(schema.returns) ? schema.returns.map((arg) => alias(arg)).filter((alias) => alias !== null) : []);
  161. const inputs = new Map();
  162. this.type = { name, identifier, category };
  163. let i = 0;
  164. const args = instr_args.args;
  165. for (; i < schema.arguments.length; i++) {
  166. const index = args[i];
  167. const arg = schema && i < schema.arguments.length ? schema.arguments[i] : null;
  168. const output = arg ? alias(schema.arguments[i]) : null;
  169. if (output && outputs.has(output)) {
  170. inputs.set(output, index);
  171. continue;
  172. }
  173. const name = arg ? arg.name : i.toString();
  174. const value = values.map(index);
  175. const argument = new executorch.Argument(name, value.value, value.type);
  176. this.inputs.push(argument);
  177. }
  178. for (let j = 0; j < schema.returns.length; j++) {
  179. const ret = schema.returns[j];
  180. const output = alias(ret);
  181. let index = args[i++];
  182. index = output && inputs.has(output) ? inputs.get(output) : index;
  183. const name = ret.name;
  184. const value = values.map(index, true);
  185. const argument = new executorch.Argument(name || '', value.value, value.type);
  186. this.outputs.push(argument);
  187. }
  188. } else if (instr_args instanceof executorch_flatbuffer.DelegateCall) {
  189. const delegate = plan.delegates[instr_args.delegate_index];
  190. const args = instr_args.args;
  191. if (!delegate.backend || !delegate.backend.type) {
  192. throw new executorch.Error(`ExecuTorch delegate '${delegate.id}' not implemented.`);
  193. }
  194. this.type = delegate.backend.type;
  195. const inputs = args.slice(0, this.type.inputs.length);
  196. for (let i = 0; i < inputs.length; i++) {
  197. const input = inputs[i];
  198. const value = values.map(input);
  199. const name = inputs.length === 1 ? 'input' : `input.${i}`;
  200. const argument = new executorch.Argument(name, value.value, value.type);
  201. this.inputs.push(argument);
  202. }
  203. const outputs = args.slice(this.type.inputs.length, this.type.inputs.length + this.type.outputs.length);
  204. for (let i = 0; i < outputs.length; i++) {
  205. const output = outputs[i];
  206. const value = values.map(output);
  207. const name = outputs.length === 1 ? 'output' : `output.${i}`;
  208. const argument = new executorch.Argument(name, value.value, value.type);
  209. this.outputs.push(argument);
  210. }
  211. for (const spec of delegate.compile_specs) {
  212. const value = spec.value instanceof Uint8Array ? new TextDecoder('utf-8').decode(spec.value) : spec.value;
  213. const attribute = new executorch.Argument(spec.key, value, 'attribute');
  214. this.attributes.push(attribute);
  215. }
  216. } else {
  217. throw new Error(`Instruction type '${instr_args.constructor.name}' not implemented.`);
  218. }
  219. }
  220. };
  221. executorch.TensorType = class {
  222. constructor(tensor) {
  223. const ScalarType = executorch.schema.executorch_flatbuffer.ScalarType;
  224. switch (tensor.scalar_type) {
  225. case ScalarType.BYTE: this.dataType = 'uint8'; break;
  226. case ScalarType.CHAR: this.dataType = 'int8'; break;
  227. case ScalarType.SHORT: this.dataType = 'int16'; break;
  228. case ScalarType.INT: this.dataType = 'int32'; break;
  229. case ScalarType.LONG: this.dataType = 'int64'; break;
  230. case ScalarType.HALF: this.dataType = 'float16'; break;
  231. case ScalarType.FLOAT: this.dataType = 'float32'; break;
  232. case ScalarType.DOUBLE: this.dataType = 'float64'; break;
  233. case ScalarType.BFLOAT16: this.dataType = 'bfloat16'; break;
  234. case 8: this.dataType = 'complex<float16>'; break;
  235. case 9: this.dataType = 'complex<float32>'; break;
  236. case 10: this.dataType = 'complex<float64>'; break;
  237. case ScalarType.BOOL: this.dataType = 'boolean'; break;
  238. case ScalarType.QINT8: this.dataType = 'qint8'; break;
  239. case ScalarType.QUINT8: this.dataType = 'quint8'; break;
  240. case ScalarType.QINT32: this.dataType = 'qint32'; break;
  241. case 15: this.dataType = 'bfloat16'; break;
  242. case ScalarType.QUINT4X2: this.dataType = 'quint4x2'; break;
  243. case ScalarType.QUINT2X4: this.dataType = 'quint2x4'; break;
  244. case 18: this.dataType = 'bits1x8'; break;
  245. case 19: this.dataType = 'bits2x4'; break;
  246. case 20: this.dataType = 'bits4x2'; break;
  247. case 21: this.dataType = 'bits8'; break;
  248. case ScalarType.BITS16: this.dataType = 'bits16'; break;
  249. case ScalarType.FLOAT8E5M2: this.dataType = 'float8e5m2'; break;
  250. case ScalarType.FLOAT8E4M3FN: this.dataType = 'float8e4m3fn'; break;
  251. case ScalarType.FLOAT8E5M2FNUZ: this.dataType = 'float8e5m2fnuz'; break;
  252. case ScalarType.FLOAT8E4M3FNUZ: this.dataType = 'float8e4m3fnuz'; break;
  253. case ScalarType.UINT16: this.dataType = 'uint16'; break;
  254. case ScalarType.UINT32: this.dataType = 'uint32'; break;
  255. case ScalarType.UINT64: this.dataType = 'uint64'; break;
  256. default: throw new executorch.Error(`Unknown tensor data type '${tensor.scalar_type}'.`);
  257. }
  258. this.shape = new executorch.TensorShape(Array.from(tensor.sizes));
  259. }
  260. toString() {
  261. return this.dataType + this.shape.toString();
  262. }
  263. };
  264. executorch.TensorShape = class {
  265. constructor(dimensions = []) {
  266. this.dimensions = dimensions;
  267. }
  268. toString() {
  269. if (this.dimensions && this.dimensions.length > 0) {
  270. return `[${this.dimensions.map((dimension) => dimension.toString()).join(',')}]`;
  271. }
  272. return '';
  273. }
  274. };
  275. executorch.Tensor = class {
  276. constructor(tensor, target) {
  277. this.type = new executorch.TensorType(tensor);
  278. const data_buffer_idx = tensor.data_buffer_idx;
  279. const program = target.program;
  280. if (tensor.extra_tensor_info) {
  281. throw new executorch.Error('Extra tensor info not implemented.');
  282. } else if (Array.isArray(program.constant_buffer) && program.constant_buffer.length > 0) {
  283. if (data_buffer_idx >= program.constant_buffer.length) {
  284. throw new executorch.Error(`Constant buffer index out of range.`);
  285. }
  286. const buffer = program.constant_buffer[data_buffer_idx];
  287. this.values = buffer.storage;
  288. this.encoding = '<';
  289. } else if (tensor.allocation_info === null) {
  290. const constant_segment = program.constant_segment;
  291. const data_segment = program.segments[constant_segment.segment_index];
  292. const offset = constant_segment.offsets[data_buffer_idx];
  293. let next = data_segment.size;
  294. if (data_buffer_idx + 1 < constant_segment.offsets.length) {
  295. next = constant_segment.offsets[data_buffer_idx + 1];
  296. }
  297. const size = next - offset;
  298. const position = data_segment.offset + offset;
  299. this.values = target.blob(position.toNumber(), size.toNumber());
  300. this.encoding = '<';
  301. } else {
  302. throw new executorch.Error('Tensor allocation info not implemented.');
  303. }
  304. }
  305. };
  306. executorch.Reader = class {
  307. static async open(context) {
  308. const reader = await context.peek('flatbuffers.binary');
  309. if (reader && reader.identifier === 'ET12') {
  310. return new executorch.Reader(context, reader);
  311. }
  312. return null;
  313. }
  314. constructor(context, reader) {
  315. this.context = context;
  316. this.reader = reader;
  317. }
  318. async read() {
  319. const context = this.context;
  320. this.metadata = await pytorch.Metadata.open(context);
  321. this.execution = new python.Execution();
  322. this.metadata.register(this.execution);
  323. const executorch_flatbuffer = executorch.schema.executorch_flatbuffer;
  324. this.program = executorch_flatbuffer.Program.create(this.reader);
  325. this.named_data = new Map();
  326. if (this.program.named_data) {
  327. this.named_data = new Map(this.program.named_data.map((entry) => [entry.key, entry.segment_index]));
  328. }
  329. this.reader = await context.read('binary');
  330. if (this.reader.length >= 32) {
  331. this.reader.seek(8);
  332. const magic = String.fromCharCode(...this.reader.read(4));
  333. if (magic === 'eh00') {
  334. this.extended_file_header = {
  335. length: this.reader.uint32(),
  336. program_size: this.reader.uint64(),
  337. segment_base_offset: this.reader.uint64(),
  338. };
  339. }
  340. this.reader.seek(0);
  341. }
  342. for (const plan of this.program.execution_plan) {
  343. for (const chain of plan.chains) {
  344. for (const instruction of chain.instructions) {
  345. const instr_args = instruction.instr_args;
  346. if (instr_args instanceof executorch_flatbuffer.DelegateCall) {
  347. const delegate = plan.delegates[instr_args.delegate_index];
  348. if (delegate.backend) {
  349. continue;
  350. }
  351. let data = null;
  352. switch (delegate.processed.location) {
  353. case executorch_flatbuffer.DataLocation.INLINE: {
  354. data = this.program.backend_delegate_data[delegate.processed.index].data;
  355. break;
  356. }
  357. case executorch_flatbuffer.DataLocation.SEGMENT: {
  358. const segment = this.program.segments[delegate.processed.index];
  359. const offset = segment.offset;
  360. const size = segment.size;
  361. data = this.blob(offset.toNumber(), size.toNumber());
  362. break;
  363. }
  364. default: {
  365. throw new executorch.Error(`Delegate data location '${delegate.processed.location}' not implemented.`);
  366. }
  367. }
  368. switch (delegate.id) {
  369. case 'XnnpackBackend':
  370. delegate.backend = xnnpack.Reader.open(data, this);
  371. break;
  372. case 'CoreMLBackend':
  373. delegate.backend = coreml.Reader.open(data, this);
  374. break;
  375. case 'VulkanBackend':
  376. delegate.backend = vulkan.Reader.open(data, this);
  377. break;
  378. case 'QnnBackend':
  379. delegate.backend = qnn.Reader.open(data, this);
  380. break;
  381. case 'EthosUBackend':
  382. delegate.backend = ethosu.Reader.open(data, this);
  383. break;
  384. case 'OpenvinoBackend':
  385. delegate.backend = openvino.Reader.open(data, this);
  386. break;
  387. case 'RockchipBackend':
  388. delegate.backend = rockchip.Reader.open(data, this);
  389. break;
  390. default:
  391. throw new executorch.Error(`ExecuTorch delegate '${delegate.id}' not implemented.`);
  392. }
  393. /* eslint-disable no-await-in-loop */
  394. await delegate.backend.read();
  395. /* eslint-enable no-await-in-loop */
  396. }
  397. }
  398. }
  399. }
  400. }
  401. blob(offset, size) {
  402. if (this.extended_file_header) {
  403. const segment_base_offset = this.extended_file_header.segment_base_offset;
  404. this.reader.seek(segment_base_offset.toNumber() + offset);
  405. const data = this.reader.read(size);
  406. this.reader.seek(0);
  407. return data;
  408. }
  409. return null;
  410. }
  411. segment(key) {
  412. if (this.named_data.has(key)) {
  413. const segment_index = this.named_data.get(key);
  414. if (segment_index >= 0 && segment_index < this.program.segments.length) {
  415. const segment = this.program.segments[segment_index];
  416. const offset = segment.offset;
  417. const size = segment.size;
  418. return this.blob(offset.toNumber(), size.toNumber());
  419. }
  420. }
  421. return null;
  422. }
  423. };
  424. executorch.Error = class extends Error {
  425. constructor(message) {
  426. super(message);
  427. this.name = 'Error loading ExecuTorch model.';
  428. }
  429. };
  430. xnnpack.Reader = class {
  431. static open(data, target) {
  432. if (data.length >= 30) {
  433. const reader = base.BinaryReader.open(data);
  434. reader.skip(4);
  435. const magic = String.fromCharCode(...reader.read(4));
  436. if (magic === 'XH00') {
  437. return new xnnpack.Reader(reader, target);
  438. }
  439. }
  440. return null;
  441. }
  442. constructor(reader, target) {
  443. this.reader = reader;
  444. this.target = target;
  445. reader.skip(2);
  446. this.flatbuffer = {
  447. offset: reader.uint32(),
  448. size: reader.uint32(),
  449. };
  450. this.constants = {
  451. offset: reader.uint32(),
  452. size: reader.uint32(),
  453. };
  454. }
  455. async read() {
  456. this.reader.seek(this.flatbuffer.offset);
  457. const flatbuffers = await import('./flatbuffers.js');
  458. const data = this.reader.read(this.flatbuffer.size);
  459. const reader = flatbuffers.BinaryReader.open(data);
  460. if (!executorch.schema.fb_xnnpack.XNNGraph.identifier(reader)) {
  461. throw new xnnpack.Error('Invalid XNNPACK data.');
  462. }
  463. this.graph = executorch.schema.fb_xnnpack.XNNGraph.create(reader);
  464. this.reader.seek(0);
  465. const metadata = new xnnpack.Metadata();
  466. this.type = new xnnpack.Graph(metadata, this.graph, this);
  467. }
  468. constant(idx) {
  469. const constant_data = this.graph.constant_data[idx];
  470. const named_key = constant_data.named_key;
  471. if (named_key) {
  472. return this.target.segment(named_key);
  473. }
  474. const offset = constant_data.offset;
  475. const size = constant_data.size;
  476. this.reader.seek(this.constants.offset + offset.toNumber());
  477. const data = this.reader.read(size.toNumber());
  478. this.reader.seek(0);
  479. return data;
  480. }
  481. };
  482. xnnpack.Graph = class {
  483. constructor(metadata, graph, reader) {
  484. this.name = 'XnnpackBackend';
  485. this.type = 'graph';
  486. this.inputs = [];
  487. this.outputs = [];
  488. this.nodes = [];
  489. const values = new Map();
  490. values.map = (id) => {
  491. if (!values.has(id)) {
  492. const fb_xnnpack = executorch.schema.fb_xnnpack;
  493. const name = id.toString();
  494. const xvalue = graph.xvalues[id].xvalue_union;
  495. if (xvalue instanceof fb_xnnpack.XNNTensorValue) {
  496. const type = new xnnpack.TensorType(xvalue);
  497. const initializer = xvalue.constant_buffer_idx === 0 ? null : new xnnpack.Tensor(xvalue, reader);
  498. const value = new xnnpack.Value(name, type, initializer);
  499. values.set(id, value);
  500. } else if (xvalue instanceof fb_xnnpack.XNNQuantizedTensorValue) {
  501. const value = new xnnpack.Value(name, null, null);
  502. values.set(id, value);
  503. } else {
  504. throw new xnnpack.Error(`Value type '${xvalue.constructor.name}' not implemented.`);
  505. }
  506. }
  507. return values.get(id);
  508. };
  509. for (let i = 0; i < graph.input_ids.length; i++) {
  510. const id = graph.input_ids[i];
  511. const value = values.map(id);
  512. const name = graph.input_ids.length === 1 ? 'input' : `input.${i}`;
  513. const argument = new xnnpack.Argument(name, [value]);
  514. this.inputs.push(argument);
  515. }
  516. for (let i = 0; i < graph.output_ids.length; i++) {
  517. const id = graph.output_ids[i];
  518. const value = values.map(id);
  519. const name = graph.output_ids.length === 1 ? 'output' : `output.${i}`;
  520. const argument = new xnnpack.Argument(name, [value]);
  521. this.outputs.push(argument);
  522. }
  523. for (const xnode of graph.xnodes) {
  524. const node = new xnnpack.Node(metadata, xnode, values);
  525. this.nodes.push(node);
  526. }
  527. }
  528. };
  529. xnnpack.Node = class {
  530. constructor(metadata, xnode, values) {
  531. const node = xnode.xnode_union;
  532. this.type = metadata.type(node.constructor.name) || { name: node.constructor.name };
  533. this.name = '';
  534. this.inputs = [];
  535. this.outputs = [];
  536. for (const [name, obj] of Object.entries(node)) {
  537. let value = ArrayBuffer.isView(obj) ? Array.from(obj) : obj;
  538. let type = 'attribute';
  539. if (name.endsWith('_id')) {
  540. value = obj === -1 || obj === 0xFFFFFFFF ? [] : [values.map(obj)];
  541. type = null;
  542. }
  543. const argument = new xnnpack.Argument(name, value, type);
  544. if (name === 'output_id') {
  545. this.outputs.push(argument);
  546. } else {
  547. this.inputs.push(argument);
  548. }
  549. }
  550. }
  551. };
  552. xnnpack.Argument = class {
  553. constructor(name, value, type = null, visible = true) {
  554. this.name = name;
  555. this.value = value;
  556. this.type = type;
  557. this.visible = visible;
  558. }
  559. };
  560. xnnpack.Value = class Value {
  561. constructor(name, type, initializer = null) {
  562. if (typeof name !== 'string') {
  563. throw new executorch.Error(`Invalid value identifier '${JSON.stringify(name)}'.`);
  564. }
  565. this.name = name;
  566. this.type = initializer && initializer.type ? initializer.type : type || null;
  567. this.initializer = initializer;
  568. }
  569. };
  570. xnnpack.Metadata = class {
  571. constructor() {
  572. this._types = new Map();
  573. this.register('_XNNCat', 'Tensor');
  574. this.register('_XNNNodeConv', 'Layer');
  575. this.register('XNNArgMaxPooling2d', 'Pool');
  576. this.register('XNNAvgPooling2d', 'Pool');
  577. this.register('XNNCeiling', 'Activation');
  578. this.register('XNNConcatenate2', 'Tensor');
  579. this.register('XNNConcatenate3', 'Tensor');
  580. this.register('XNNConcatenate4', 'Tensor');
  581. this.register('XNNConcatenate5', 'Tensor');
  582. this.register('XNNConv2d', 'Layer');
  583. this.register('XNNConvTranspose2d', 'Layer');
  584. this.register('XNNDepthwiseConv2d', 'Layer');
  585. this.register('XNNELU', 'Activation');
  586. this.register('XNNFullyConnected', 'Layer');
  587. this.register('XNNGelu', 'Activation');
  588. this.register('XNNGlobalAvgPooling2d', 'Pool');
  589. this.register('XNNGlobalAvgPooling2d', 'Pool');
  590. this.register('XNNHardswish', 'Activation');
  591. this.register('XNNLeakyReLU', 'Activation');
  592. this.register('XNNMaxPooling2d', 'Pool');
  593. this.register('XNNPReLU', 'Activation');
  594. this.register('XNNSigmoid', 'Activation');
  595. this.register('XNNSoftmax', 'Activation');
  596. this.register('XNNTanh', 'Activation');
  597. this.register('XNNStaticTranspose', 'Transform');
  598. }
  599. register(name, category) {
  600. this._types.set(name, { name, category });
  601. }
  602. type(name) {
  603. return this._types.get(name);
  604. }
  605. };
  606. xnnpack.TensorType = class {
  607. constructor(tensor) {
  608. xnnpack.TensorType._types = executorch.TensorType._types || [
  609. 'invalid', 'float32', 'float16',
  610. 'qint8', 'quint8', 'qint32',
  611. 'qcint8', 'qcint32', 'qcint4',
  612. 'qdint8', 'qbint4', 'qpint8',
  613. 'int32', 'pfp32', 'bfloat16'
  614. ];
  615. if (tensor.datatype >= xnnpack.TensorType._types.length) {
  616. throw new xnnpack.Error(`Unknown tensor data type '${tensor.datatype}'.`);
  617. }
  618. this.dataType = xnnpack.TensorType._types[tensor.datatype];
  619. this.shape = new xnnpack.TensorShape(Array.from(tensor.dims));
  620. }
  621. toString() {
  622. return this.dataType + this.shape.toString();
  623. }
  624. };
  625. xnnpack.TensorShape = class {
  626. constructor(dimensions = []) {
  627. this.dimensions = dimensions;
  628. }
  629. toString() {
  630. if (this.dimensions && this.dimensions.length > 0) {
  631. return `[${this.dimensions.map((dimension) => dimension.toString()).join(',')}]`;
  632. }
  633. return '';
  634. }
  635. };
  636. xnnpack.Tensor = class {
  637. constructor(tensor, reader) {
  638. this.type = new xnnpack.TensorType(tensor);
  639. this.values = reader.constant(tensor.constant_buffer_idx);
  640. this.encoding = '<';
  641. }
  642. };
  643. xnnpack.Error = class extends Error {
  644. constructor(message) {
  645. super(message);
  646. this.name = 'Error loading XNNPACK model.';
  647. }
  648. };
  649. vulkan.Reader = class {
  650. static open(data, target) {
  651. if (data.length >= 30) {
  652. const reader = base.BinaryReader.open(data);
  653. reader.skip(4);
  654. const magic = String.fromCharCode(...reader.read(4));
  655. if (magic === 'VH00') {
  656. return new vulkan.Reader(reader, target);
  657. }
  658. }
  659. return null;
  660. }
  661. constructor(reader, target) {
  662. this.reader = reader;
  663. this.target = target;
  664. reader.skip(2);
  665. this.flatbuffer = {
  666. offset: reader.uint32(),
  667. size: reader.uint32(),
  668. };
  669. this.constants = {
  670. offset: reader.uint32(),
  671. size: reader.uint32(),
  672. };
  673. }
  674. async read() {
  675. this.reader.seek(this.flatbuffer.offset);
  676. const metadata = new vulkan.Metadata(this.target.execution);
  677. metadata.register('conv_with_clamp(Tensor input, Tensor weight, Tensor? bias, SymInt[] stride, SymInt[] padding, SymInt[] dilation, bool transposed, SymInt[] output_padding, SymInt groups, Scalar? output_min, Scalar? output_max) -> Tensor)');
  678. const flatbuffers = await import('./flatbuffers.js');
  679. const data = this.reader.read(this.flatbuffer.size);
  680. const reader = flatbuffers.BinaryReader.open(data);
  681. if (!executorch.schema.vkgraph.VkGraph.identifier(reader)) {
  682. throw new xnnpack.Error('Invalid Vuklan data.');
  683. }
  684. this.graph = executorch.schema.vkgraph.VkGraph.create(reader);
  685. this.reader.seek(0);
  686. this.type = new vulkan.Graph(metadata, this.graph, this);
  687. }
  688. constant(id) {
  689. const constant = this.graph.constants[id];
  690. const offset = constant.offset;
  691. const length = constant.length;
  692. this.reader.seek(this.constants.offset + offset.toNumber());
  693. const data = this.reader.read(length.toNumber());
  694. this.reader.seek(0);
  695. return data;
  696. }
  697. };
  698. vulkan.Graph = class {
  699. constructor(metadata, graph, reader) {
  700. this.name = 'VulkanBackend';
  701. this.inputs = [];
  702. this.outputs = [];
  703. this.nodes = [];
  704. const values = new Map();
  705. values.map = (id) => {
  706. if (!values.has(id)) {
  707. const vkgraph = executorch.schema.vkgraph;
  708. const arg = graph.values[id].value;
  709. if (arg instanceof vkgraph.VkTensor) {
  710. const type = new vulkan.TensorType(arg);
  711. const initializer = arg.constant_id === -1 ? null : new vulkan.Tensor(arg, reader);
  712. const value = new vulkan.Value(id.toString(), type, initializer);
  713. values.set(id, { type: null, value: [value] });
  714. } else if (arg instanceof vkgraph.Int) {
  715. values.set(id, { type: 'int64', value: arg.int_val });
  716. } else if (arg instanceof vkgraph.IntList) {
  717. values.set(id, { type: 'int64[]', value: Array.from(arg.items) });
  718. } else if (arg instanceof vkgraph.Double) {
  719. values.set(id, { type: 'float64', value: arg.double_val });
  720. } else if (arg instanceof vkgraph.Bool) {
  721. values.set(id, { type: 'boolean', value: arg.bool_val });
  722. } else if (arg instanceof vkgraph.Null) {
  723. values.set(id, { type: 'attribute', value: null });
  724. } else {
  725. throw new Error(`Value type '${arg.constructor.name}' not implemented.`);
  726. }
  727. }
  728. return values.get(id);
  729. };
  730. for (let i = 0; i < graph.input_ids.length; i++) {
  731. const id = graph.input_ids[i];
  732. const value = values.map(id);
  733. const name = graph.input_ids.length === 1 ? 'input' : `input.${i}`;
  734. const argument = new vulkan.Argument(name, value.value, value.type);
  735. this.inputs.push(argument);
  736. }
  737. for (let i = 0; i < graph.output_ids.length; i++) {
  738. const id = graph.output_ids[i];
  739. const value = values.map(id);
  740. const name = graph.output_ids.length === 1 ? 'output' : `output.${i}`;
  741. const argument = new vulkan.Argument(name, value.value, value.type);
  742. this.outputs.push(argument);
  743. }
  744. for (const op of graph.chain) {
  745. const node = new vulkan.Node(metadata, op, values);
  746. this.nodes.push(node);
  747. }
  748. }
  749. };
  750. vulkan.Node = class {
  751. constructor(metadata, op, values) {
  752. const schema = metadata.type(op.name);
  753. if (!schema) {
  754. throw new vulkan.Error(`Operator schema for '${op.name}' not found.`);
  755. }
  756. this.type = {
  757. name: op.name.split(/\.([^.]*)$/)[0],
  758. identifier: op.name,
  759. category: schema.category || ''
  760. };
  761. this.name = op.node_id.toString();
  762. this.inputs = [];
  763. this.outputs = [];
  764. this.attributes = [];
  765. for (let i = 0; i < op.args.length; i++) {
  766. const arg = op.args[i];
  767. const input = schema && i < schema.arguments.length;
  768. const def = input ? schema.arguments[i] : schema.returns[i - schema.arguments.length];
  769. const value = values.map(arg);
  770. const argument = new vulkan.Argument(def.name || '', value.value, value.type);
  771. if (input) {
  772. this.inputs.push(argument);
  773. } else {
  774. this.outputs.push(argument);
  775. }
  776. }
  777. }
  778. };
  779. vulkan.Argument = class {
  780. constructor(name, value, type = null, visible = true) {
  781. this.name = name;
  782. this.value = value;
  783. this.type = type;
  784. this.visible = visible;
  785. }
  786. };
  787. vulkan.Value = class Value {
  788. constructor(name, type, initializer = null) {
  789. if (typeof name !== 'string') {
  790. throw new executorch.Error(`Invalid value identifier '${JSON.stringify(name)}'.`);
  791. }
  792. this.name = name;
  793. this.type = initializer && initializer.type ? initializer.type : type || null;
  794. this.initializer = initializer;
  795. }
  796. };
  797. vulkan.TensorType = class {
  798. constructor(tensor) {
  799. const types = ['bool', 'uint8', 'int8', 'int32', 'float16', 'float32', 'float64', 'int64'];
  800. if (tensor.datatype >= types.length) {
  801. throw new vulkan.Error(`Unknown tensor data type '${tensor.datatype}'.`);
  802. }
  803. this.dataType = types[tensor.datatype];
  804. this.shape = new vulkan.TensorShape(Array.from(tensor.dims));
  805. }
  806. toString() {
  807. return this.dataType + this.shape.toString();
  808. }
  809. };
  810. vulkan.TensorShape = class {
  811. constructor(dimensions = []) {
  812. this.dimensions = dimensions;
  813. }
  814. toString() {
  815. if (this.dimensions && this.dimensions.length > 0) {
  816. return `[${this.dimensions.map((dimension) => dimension.toString()).join(',')}]`;
  817. }
  818. return '';
  819. }
  820. };
  821. vulkan.Tensor = class {
  822. constructor(tensor, reader) {
  823. this.type = new vulkan.TensorType(tensor);
  824. this.values = reader.constant(tensor.constant_id);
  825. this.encoding = '<';
  826. }
  827. };
  828. vulkan.Metadata = class {
  829. constructor(execution) {
  830. this.execution = execution;
  831. }
  832. register(signature) {
  833. const torch = this.execution.__import__('torch');
  834. const registry = torch._C.getRegistry();
  835. const schema = torch.FunctionSchema.parse(signature);
  836. const op = new torch._C.Operator(schema);
  837. registry.registerOperator(op);
  838. }
  839. type(identifier) {
  840. identifier = identifier.split(/\.([^.]*)$/);
  841. const name = identifier[0].replace('.', '::');
  842. const overload = identifier[1] === 'default' ? '' : identifier[1];
  843. const torch = this.execution.__import__('torch');
  844. const schemas = torch._C._jit_get_schemas_for_operator(name);
  845. const schema = schemas.find((schema) => schema.name === name && schema.overload_name === overload);
  846. return schema;
  847. }
  848. };
  849. vulkan.Error = class extends Error {
  850. constructor(message) {
  851. super(message);
  852. this.name = 'Error loading Vulkan model.';
  853. }
  854. };
  855. coreml.Reader = class {
  856. static open(data, target) {
  857. const reader = base.BinaryReader.open(data);
  858. return new coreml.Reader(reader, target);
  859. }
  860. constructor(reader, target) {
  861. this.reader = reader;
  862. this.target = target;
  863. }
  864. async factory() {
  865. const coreml = await import('./coreml.js');
  866. return new coreml.ModelFactory();
  867. }
  868. async read() {
  869. const entries = this.entries(this.reader);
  870. const factory = await this.factory();
  871. const protobuf = await import('./protobuf.js');
  872. for (const [key, value] of entries) {
  873. const path = key.split('/');
  874. const identifier = path.pop();
  875. const folder = path.length === 0 ? '' : `${path.join('/')}/`;
  876. const locals = new Map(Array.from(entries).filter(([key]) => key.startsWith(folder)).map(([key, value]) => [key.substring(folder.length), value]));
  877. const context = new coreml.Context(this, identifier, value, locals, protobuf);
  878. /* eslint-disable no-await-in-loop */
  879. const type = await factory.match(context);
  880. /* eslint-enable no-await-in-loop */
  881. if (type === 'coreml.manifest') {
  882. /* eslint-disable no-await-in-loop */
  883. const model = await factory.open(context);
  884. /* eslint-enable no-await-in-loop */
  885. [this.type] = model.modules;
  886. this.type.name = 'CoreMLBackend';
  887. return;
  888. }
  889. }
  890. }
  891. stream(offset, size) {
  892. this.reader.seek(offset);
  893. const stream = this.reader.stream(size);
  894. this.reader.seek(0);
  895. return stream;
  896. }
  897. entries(reader) {
  898. const files = new Map();
  899. reader.seek(reader.length - 1);
  900. const str = [];
  901. let depth = 0;
  902. do {
  903. const c = String.fromCharCode(reader.byte());
  904. reader.skip(-2);
  905. if (c === '{') {
  906. depth++;
  907. } else if (c === '}') {
  908. depth--;
  909. }
  910. str.push(c);
  911. } while (depth > 0);
  912. const metadata = JSON.parse(str.join(''));
  913. const nodes = metadata.nodes;
  914. const roots = Array.from(nodes);
  915. for (const root of roots) {
  916. if (root !== null) {
  917. for (const index of Object.values(root.children)) {
  918. roots[index] = null;
  919. }
  920. }
  921. }
  922. const process = (path, node) => {
  923. path = path ? `${path}/${node.name}` : node.name;
  924. if (node.kind === 0) {
  925. files.set(path, node.dataRegion);
  926. } else if (node.kind === 1) {
  927. for (const index of Object.values(node.children)) {
  928. process(path, nodes[index]);
  929. }
  930. } else {
  931. throw new Error(`Node kind '${node.kind}' not implemented.`);
  932. }
  933. };
  934. for (const root of roots.filter((node) => node !== null)) {
  935. process('', root);
  936. }
  937. return files;
  938. }
  939. };
  940. coreml.Context = class {
  941. constructor(reader, identifier, location, entries, protobuf) {
  942. this._reader = reader;
  943. this._location = location;
  944. this._identifier = identifier;
  945. this._entries = entries;
  946. this._protobuf = protobuf;
  947. }
  948. get identifier() {
  949. return this._identifier;
  950. }
  951. get stream() {
  952. if (!this._stream) {
  953. this._stream = this._reader.stream(this._location.offset, this._location.size);
  954. }
  955. return this._stream;
  956. }
  957. async tags(type) {
  958. if (type === 'pb' && this.identifier.endsWith('.mlmodel')) {
  959. return new Map([[1,0],[2,2]]);
  960. }
  961. return new Map();
  962. }
  963. async peek(type) {
  964. if (type === 'json') {
  965. const data = this.stream.peek();
  966. const decoder = new TextDecoder('utf-8');
  967. const text = decoder.decode(data);
  968. return JSON.parse(text);
  969. }
  970. return null;
  971. }
  972. async read(type) {
  973. if (type === 'protobuf.binary') {
  974. return this._protobuf.BinaryReader.open(this.stream);
  975. }
  976. return null;
  977. }
  978. async fetch(file) {
  979. if (this._entries.has(file)) {
  980. const location = this._entries.get(file);
  981. const identifier = file.split('/').pop();
  982. return new coreml.Context(this._reader, identifier, location, this._entries, this._protobuf);
  983. }
  984. return null;
  985. }
  986. async require(id) {
  987. return this._reader.target.context.require(id);
  988. }
  989. async metadata(name) {
  990. return this._reader.target.context.metadata(name);
  991. }
  992. set(type, value) {
  993. this.type = type;
  994. this.value = value;
  995. return type;
  996. }
  997. };
  998. qnn.Reader = class {
  999. static open(data, target) {
  1000. if (data.length >= 20) {
  1001. const reader = base.BinaryReader.open(data);
  1002. const magic = reader.uint32();
  1003. if (magic === 0x5678ABCD) {
  1004. return new qnn.Reader(reader, target);
  1005. }
  1006. }
  1007. return null;
  1008. }
  1009. constructor(reader, target) {
  1010. this.reader = reader;
  1011. this.target = target;
  1012. this.signature = reader.uint64();
  1013. this.size = reader.uint64();
  1014. }
  1015. async read() {
  1016. // https://github.com/pytorch/executorch/blob/main/backends/qualcomm/runtime/backends/QnnCustomProtocol.h
  1017. throw new executorch.Error('QNN backend not implemented.');
  1018. }
  1019. };
  1020. qnn.Graph = class {
  1021. constructor() {
  1022. this.name = 'QnnBackend';
  1023. this.inputs = [];
  1024. this.outputs = [];
  1025. this.nodes = [];
  1026. }
  1027. };
  1028. ethosu.Reader = class {
  1029. static open(data /* , target */) {
  1030. if (data.length >= 32) {
  1031. const reader = base.BinaryReader.open(data);
  1032. const magicBuffer = reader.read(16);
  1033. const magic = String.fromCharCode(...magicBuffer).replace(/\0/g, '');
  1034. if (magic === 'vela_bin_stream') {
  1035. return new ethosu.Reader(reader, data.length);
  1036. }
  1037. }
  1038. return null;
  1039. }
  1040. constructor(reader, size) {
  1041. this.reader = reader;
  1042. this.size = size;
  1043. }
  1044. async read() {
  1045. this.reader.seek(0);
  1046. const blocks = new Map();
  1047. while (this.reader.position < this.size) {
  1048. const nameBuffer = this.reader.read(16);
  1049. const name = String.fromCharCode(...nameBuffer).replace(/\0/g, '');
  1050. const size = this.reader.uint32();
  1051. this.reader.skip(12);
  1052. const data = this.reader.read(size);
  1053. blocks.set(name, data);
  1054. const padding = (16 - (size % 16)) % 16;
  1055. this.reader.skip(padding);
  1056. if (name === 'vela_end_stream') {
  1057. break;
  1058. }
  1059. }
  1060. const args = (data) => {
  1061. if (data && data.length >= 4) {
  1062. const reader = base.BinaryReader.open(data);
  1063. const count = reader.int32();
  1064. const arg = [];
  1065. for (let i = 0; i < count; i++) {
  1066. const shape = [];
  1067. for (let j = 0; j < 6; j++) {
  1068. shape.push(reader.int32());
  1069. }
  1070. const elem_size = reader.int32();
  1071. const offset = reader.int32();
  1072. const region = reader.int32();
  1073. arg.push({ shape, elem_size, offset, region });
  1074. }
  1075. return arg;
  1076. }
  1077. return [];
  1078. };
  1079. const inputs = args(blocks.get('inputs'));
  1080. const outputs = args(blocks.get('outputs'));
  1081. this.type = new ethosu.Graph(inputs, outputs);
  1082. }
  1083. };
  1084. ethosu.Graph = class {
  1085. constructor(inputs, outputs) {
  1086. this.name = 'EthosUBackend';
  1087. this.inputs = [];
  1088. this.outputs = [];
  1089. this.nodes = [];
  1090. for (let i = 0; i < inputs.length; i++) {
  1091. const input = inputs[i];
  1092. const type = new ethosu.TensorType(input);
  1093. const value = new ethosu.Value(i.toString(), type, null);
  1094. const name = inputs.length === 1 ? 'input' : `input.${i}`;
  1095. const argument = new ethosu.Argument(name, [value]);
  1096. this.inputs.push(argument);
  1097. }
  1098. for (let i = 0; i < outputs.length; i++) {
  1099. const output = outputs[i];
  1100. const type = new ethosu.TensorType(output);
  1101. const value = new ethosu.Value((inputs.length + i).toString(), type, null);
  1102. const name = outputs.length === 1 ? 'output' : `output.${i}`;
  1103. const argument = new ethosu.Argument(name, [value]);
  1104. this.outputs.push(argument);
  1105. }
  1106. }
  1107. };
  1108. ethosu.Argument = class {
  1109. constructor(name, value, type = null, visible = true) {
  1110. this.name = name;
  1111. this.value = value;
  1112. this.type = type;
  1113. this.visible = visible;
  1114. }
  1115. };
  1116. ethosu.Value = class Value {
  1117. constructor(name, type, initializer = null) {
  1118. if (typeof name !== 'string') {
  1119. throw new executorch.Error(`Invalid value identifier '${JSON.stringify(name)}'.`);
  1120. }
  1121. this.name = name;
  1122. this.type = initializer && initializer.type ? initializer.type : type || null;
  1123. this.initializer = initializer;
  1124. }
  1125. };
  1126. ethosu.TensorType = class {
  1127. constructor(io) {
  1128. switch (io.elem_size) {
  1129. case 1: this.dataType = 'int8'; break;
  1130. case 2: this.dataType = 'int16'; break;
  1131. case 4: this.dataType = 'int32'; break;
  1132. default: this.dataType = `?${io.elem_size}`; break;
  1133. }
  1134. const shape = io.shape.filter((dim, index) => dim !== 1 || index === io.shape.length - 1 || io.shape.slice(index).some((d) => d !== 1));
  1135. this.shape = new ethosu.TensorShape(shape.length > 0 ? shape : [1]);
  1136. }
  1137. toString() {
  1138. return this.dataType + this.shape.toString();
  1139. }
  1140. };
  1141. ethosu.TensorShape = class {
  1142. constructor(dimensions = []) {
  1143. this.dimensions = dimensions;
  1144. }
  1145. toString() {
  1146. if (this.dimensions && this.dimensions.length > 0) {
  1147. return `[${this.dimensions.map((dimension) => dimension.toString()).join(',')}]`;
  1148. }
  1149. return '';
  1150. }
  1151. };
  1152. ethosu.Error = class extends Error {
  1153. constructor(message) {
  1154. super(message);
  1155. this.name = 'Error loading Ethos-U model.';
  1156. }
  1157. };
  1158. openvino.Reader = class {
  1159. static open(data /* , target */) {
  1160. return new openvino.Reader(data);
  1161. }
  1162. constructor(data) {
  1163. this.data = data;
  1164. }
  1165. async read() {
  1166. throw new executorch.Error('OpenVINO backend not implemented.');
  1167. }
  1168. };
  1169. openvino.Graph = class {
  1170. constructor() {
  1171. this.name = 'OpenvinoBackend';
  1172. this.inputs = [];
  1173. this.outputs = [];
  1174. this.nodes = [];
  1175. }
  1176. };
  1177. rockchip.Reader = class {
  1178. static open(data /* , target */) {
  1179. return new rockchip.Reader(data);
  1180. }
  1181. constructor(data) {
  1182. this.data = data;
  1183. }
  1184. async read() {
  1185. throw new executorch.Error('Rockchip backend not implemented.');
  1186. }
  1187. };
  1188. rockchip.Graph = class {
  1189. constructor() {
  1190. this.name = 'RockchipBackend';
  1191. this.inputs = [];
  1192. this.outputs = [];
  1193. this.nodes = [];
  1194. }
  1195. };
  1196. export const ModelFactory = executorch.ModelFactory;