Lutz Roeder 3 лет назад
Родитель
Сommit
67716e7a30
24 измененных файлов с 513 добавлено и 799 удалено
  1. 38 19
      source/base.js
  2. 39 11
      source/bigdl.js
  3. 1 1
      source/caffe.js
  4. 1 1
      source/caffe2.js
  5. 1 1
      source/circle.js
  6. 1 1
      source/cntk.js
  7. 23 118
      source/coreml.js
  8. 1 1
      source/dlc.js
  9. 1 1
      source/flax.js
  10. 1 1
      source/keras.js
  11. 1 1
      source/mnn.js
  12. 1 1
      source/mslite.js
  13. 1 1
      source/nnabla.js
  14. 1 1
      source/numpy.js
  15. 48 289
      source/onnx.js
  16. 1 1
      source/python.js
  17. 90 29
      source/pytorch.js
  18. 1 1
      source/sklearn.js
  19. 21 11
      source/tf.js
  20. 1 1
      source/tflite.js
  21. 1 1
      source/torch.js
  22. 219 264
      source/view-sidebar.js
  23. 6 17
      source/view.js
  24. 14 26
      test/models.js

+ 38 - 19
source/base.js

@@ -543,6 +543,25 @@ DataView.prototype.setInt64 = DataView.prototype.setInt64 || function(byteOffset
     }
 };
 
+DataView.prototype.getIntBits = DataView.prototype.getUintBits || function(offset, bits) {
+    offset = offset * bits;
+    const available = (this.byteLength << 3) - offset;
+    if (bits > available) {
+        throw new RangeError();
+    }
+    let value = 0;
+    let index = 0;
+    while (index < bits) {
+        const remainder = offset & 7;
+        const size = Math.min(bits - index, 8 - remainder);
+        value <<= size;
+        value |= (this.getUint8(offset >> 3) >> (8 - size - remainder)) & ~(0xff << size);
+        offset += size;
+        index += size;
+    }
+    return (value < (2 << (bits - 1)) ? value : (2 << bits));
+};
+
 DataView.prototype.getUint64 = DataView.prototype.getUint64 || function(byteOffset, littleEndian) {
     return littleEndian ?
         new base.Uint64(this.getUint32(byteOffset, true), this.getUint32(byteOffset + 4, true)) :
@@ -560,6 +579,25 @@ DataView.prototype.setUint64 = DataView.prototype.setUint64 || function(byteOffs
     }
 };
 
+DataView.prototype.getUintBits = DataView.prototype.getUintBits || function(offset, bits) {
+    offset = offset * bits;
+    const available = (this.byteLength << 3) - offset;
+    if (bits > available) {
+        throw new RangeError();
+    }
+    let value = 0;
+    let index = 0;
+    while (index < bits) {
+        const remainder = offset & 7;
+        const size = Math.min(bits - index, 8 - remainder);
+        value <<= size;
+        value |= (this.getUint8(offset >> 3) >> (8 - size - remainder)) & ~(0xff << size);
+        offset += size;
+        index += size;
+    }
+    return value;
+};
+
 DataView.prototype.getComplex64 = DataView.prototype.getComplex64 || function(byteOffset, littleEndian) {
     const real = littleEndian ? this.getFloat32(byteOffset, littleEndian) : this.getFloat32(byteOffset + 4, littleEndian);
     const imaginary = littleEndian ? this.getFloat32(byteOffset + 4, littleEndian) : this.getFloat32(byteOffset, littleEndian);
@@ -594,25 +632,6 @@ DataView.prototype.setComplex128 = DataView.prototype.setComplex128 || function(
     }
 };
 
-DataView.prototype.getBits = DataView.prototype.getBits || function(offset, bits /*, signed */) {
-    offset = offset * bits;
-    const available = (this.byteLength << 3) - offset;
-    if (bits > available) {
-        throw new RangeError();
-    }
-    let value = 0;
-    let index = 0;
-    while (index < bits) {
-        const remainder = offset & 7;
-        const size = Math.min(bits - index, 8 - remainder);
-        value <<= size;
-        value |= (this.getUint8(offset >> 3) >> (8 - size - remainder)) & ~(0xff << size);
-        offset += size;
-        index += size;
-    }
-    return value;
-};
-
 base.BinaryReader = class {
 
     constructor(data) {

+ 39 - 11
source/bigdl.js

@@ -58,15 +58,16 @@ bigdl.Graph = class {
         this._inputs = [];
         this._outputs = [];
         this._nodes = [];
-        this._loadModule(metadata, module);
+        const tensors = module.attr && module.attr.global_storage && module.attr.global_storage.nameAttrListValue && module.attr.global_storage.nameAttrListValue.attr ? module.attr.global_storage.nameAttrListValue.attr : {};
+        this._loadModule(metadata, module, tensors);
     }
 
-    _loadModule(metadata, module) {
+    _loadModule(metadata, module, tensors) {
         switch (module.moduleType) {
             case 'com.intel.analytics.bigdl.nn.StaticGraph':
             case 'com.intel.analytics.bigdl.nn.Sequential': {
                 for (const submodule of module.subModules) {
-                    this._loadModule(metadata, submodule);
+                    this._loadModule(metadata, submodule, tensors);
                 }
                 break;
             }
@@ -77,7 +78,7 @@ bigdl.Graph = class {
                 break;
             }
             default: {
-                this._nodes.push(new bigdl.Node(metadata, module));
+                this._nodes.push(new bigdl.Node(metadata, module, tensors));
                 break;
             }
         }
@@ -149,7 +150,7 @@ bigdl.Argument = class {
 
 bigdl.Node = class {
 
-    constructor(metadata, module) {
+    constructor(metadata, module, tensors) {
         const type = module.moduleType;
         this._name = module.name;
         this._attributes = [];
@@ -162,13 +163,13 @@ bigdl.Node = class {
         if (module.weight) {
             inputs.shift();
             this._inputs.push(new bigdl.Parameter('weight', [
-                new bigdl.Argument('', null, new bigdl.Tensor(module.weight))
+                new bigdl.Argument('', null, new bigdl.Tensor(module.weight, tensors))
             ]));
         }
         if (module.bias) {
             inputs.shift();
             this._inputs.push(new bigdl.Parameter('bias', [
-                new bigdl.Argument('', null, new bigdl.Tensor(module.bias))
+                new bigdl.Argument('', null, new bigdl.Tensor(module.bias, tensors))
             ]));
         }
         if (module.parameters && module.parameters.length > 0) {
@@ -176,7 +177,7 @@ bigdl.Node = class {
                 const input = inputs.shift();
                 const inputName = input ? input.name : this._inputs.length.toString();
                 this._inputs.push(new bigdl.Parameter(inputName, [
-                    new bigdl.Argument('', null, new bigdl.Tensor(parameter))
+                    new bigdl.Argument('', null, new bigdl.Tensor(parameter, tensors))
                 ]));
             }
         }
@@ -187,7 +188,7 @@ bigdl.Node = class {
             }
             if (value.dataType === bigdl.proto.DataType.TENSOR) {
                 if (value.value) {
-                    this._inputs.push(new bigdl.Parameter(key, [ new bigdl.Argument('', null, new bigdl.Tensor(value.tensorValue)) ]));
+                    this._inputs.push(new bigdl.Parameter(key, [ new bigdl.Argument('', null, new bigdl.Tensor(value.tensorValue, tensors)) ]));
                 }
                 continue;
             }
@@ -195,7 +196,7 @@ bigdl.Node = class {
                 continue;
             }
             if (value.dataType === bigdl.proto.DataType.ARRAY_VALUE && value.arrayValue.datatype === bigdl.proto.DataType.TENSOR) {
-                this._inputs.push(new bigdl.Parameter(key, value.arrayValue.tensor.map((tensor) => new bigdl.Argument('', null, new bigdl.Tensor(tensor)))));
+                this._inputs.push(new bigdl.Parameter(key, value.arrayValue.tensor.map((tensor) => new bigdl.Argument('', null, new bigdl.Tensor(tensor, tensors)))));
                 continue;
             }
             this._attributes.push(new bigdl.Attribute(key, value));
@@ -326,8 +327,27 @@ bigdl.Attribute = class {
 
 bigdl.Tensor = class {
 
-    constructor(tensor) {
+    constructor(tensor /*, tensors */) {
         this._type = new bigdl.TensorType(tensor.datatype, new bigdl.TensorShape(tensor.size));
+        /*
+        if (tensor && tensor.id && tensors && tensors[tensor.id] && tensors[tensor.id].tensorValue && tensors[tensor.id].tensorValue.storage) {
+            const storage = tensors[tensor.id].tensorValue.storage;
+            switch (this._type.dataType) {
+                case 'float32':
+                    if (storage.bytes_data && storage.bytes_data.length > 0) {
+                        this._values = storage.bytes_data[0];
+                        this._layout = '<';
+                    }
+                    else if (storage.float_data && storage.float_data.length > 0) {
+                        this._values = storage.float_data;
+                        this._layout = '|';
+                    }
+                    break;
+                default:
+                    break;
+            }
+        }
+        */
     }
 
     get category() {
@@ -337,6 +357,14 @@ bigdl.Tensor = class {
     get type() {
         return this._type;
     }
+
+    get layout() {
+        return this._layout;
+    }
+
+    get values() {
+        return this._values;
+    }
 };
 
 bigdl.TensorType = class {

+ 1 - 1
source/caffe.js

@@ -646,7 +646,7 @@ caffe.Tensor = class {
         return this._type;
     }
 
-    get encoding() {
+    get layout() {
         return '|';
     }
 

+ 1 - 1
source/caffe2.js

@@ -600,7 +600,7 @@ caffe2.Tensor = class {
         return null;
     }
 
-    get encoding() {
+    get layout() {
         return '|';
     }
 

+ 1 - 1
source/circle.js

@@ -567,7 +567,7 @@ circle.Tensor = class {
         return this._type;
     }
 
-    get encoding() {
+    get layout() {
         switch (this._type.dataType) {
             case 'string': return '|';
             default: return '<';

+ 1 - 1
source/cntk.js

@@ -560,7 +560,7 @@ cntk.Tensor = class {
         return this._type;
     }
 
-    get encoding() {
+    get layout() {
         return '|';
     }
 

+ 23 - 118
source/coreml.js

@@ -984,29 +984,29 @@ coreml.Graph = class {
             case 'uniDirectionalLSTM':
             case 'biDirectionalLSTM': {
                 const count = (type == 'uniDirectionalLSTM') ? 1 : 2;
-                const matrixShape = [ data.outputVectorSize, data.inputVectorSize ];
-                const vectorShape = [ data.outputVectorSize ];
+                const h = data.outputVectorSize;
+                const x = data.inputVectorSize;
                 for (let i = 0; i < count; i++) {
                     const weights = count == 1 ? data.weightParams : data.weightParams[i];
                     const suffix = (i == 0) ? '' : '_rev';
-                    this._initializer(type, initializers, 'Weights', 'inputGateWeightMatrix' + suffix, matrixShape, weights.inputGateWeightMatrix);
-                    this._initializer(type, initializers, 'Weights', 'forgetGateWeightMatrix' + suffix, matrixShape, weights.forgetGateWeightMatrix);
-                    this._initializer(type, initializers, 'Weights', 'blockInputWeightMatrix' + suffix, matrixShape, weights.blockInputWeightMatrix);
-                    this._initializer(type, initializers, 'Weights', 'outputGateWeightMatrix' + suffix, matrixShape, weights.outputGateWeightMatrix);
-                    this._initializer(type, initializers, 'Weights', 'inputGateRecursionMatrix' + suffix, matrixShape, weights.inputGateRecursionMatrix);
-                    this._initializer(type, initializers, 'Weights', 'forgetGateRecursionMatrix' + suffix, matrixShape,weights.forgetGateRecursionMatrix);
-                    this._initializer(type, initializers, 'Weights', 'blockInputRecursionMatrix' + suffix, matrixShape, weights.blockInputRecursionMatrix);
-                    this._initializer(type, initializers, 'Weights', 'outputGateRecursionMatrix' + suffix, matrixShape, weights.outputGateRecursionMatrix);
+                    this._initializer(type, initializers, 'Weights', 'inputGateWeightMatrix' + suffix, [h,x], weights.inputGateWeightMatrix);
+                    this._initializer(type, initializers, 'Weights', 'forgetGateWeightMatrix' + suffix, [h,x], weights.forgetGateWeightMatrix);
+                    this._initializer(type, initializers, 'Weights', 'blockInputWeightMatrix' + suffix, [h,x], weights.blockInputWeightMatrix);
+                    this._initializer(type, initializers, 'Weights', 'outputGateWeightMatrix' + suffix, [h,x], weights.outputGateWeightMatrix);
+                    this._initializer(type, initializers, 'Weights', 'inputGateRecursionMatrix' + suffix, [h,h], weights.inputGateRecursionMatrix);
+                    this._initializer(type, initializers, 'Weights', 'forgetGateRecursionMatrix' + suffix, [h,h],weights.forgetGateRecursionMatrix);
+                    this._initializer(type, initializers, 'Weights', 'blockInputRecursionMatrix' + suffix, [h,h], weights.blockInputRecursionMatrix);
+                    this._initializer(type, initializers, 'Weights', 'outputGateRecursionMatrix' + suffix, [h,h], weights.outputGateRecursionMatrix);
                     if (data.params.hasBiasVectors) {
-                        this._initializer(type, initializers, 'Weights', 'inputGateBiasVector' + suffix, vectorShape, weights.inputGateBiasVector);
-                        this._initializer(type, initializers, 'Weights', 'forgetGateBiasVector' + suffix, vectorShape, weights.forgetGateBiasVector);
-                        this._initializer(type, initializers, 'Weights', 'blockInputBiasVector' + suffix, vectorShape, weights.blockInputBiasVector);
-                        this._initializer(type, initializers, 'Weights', 'outputGateBiasVector' + suffix, vectorShape, weights.outputGateBiasVector);
+                        this._initializer(type, initializers, 'Weights', 'inputGateBiasVector' + suffix, [h], weights.inputGateBiasVector);
+                        this._initializer(type, initializers, 'Weights', 'forgetGateBiasVector' + suffix, [h], weights.forgetGateBiasVector);
+                        this._initializer(type, initializers, 'Weights', 'blockInputBiasVector' + suffix, [h], weights.blockInputBiasVector);
+                        this._initializer(type, initializers, 'Weights', 'outputGateBiasVector' + suffix, [h], weights.outputGateBiasVector);
                     }
                     if (data.params.hasPeepholeVectors) {
-                        this._initializer(type, initializers, 'Weights', 'inputGatePeepholeVector' + suffix, vectorShape, weights.inputGatePeepholeVector);
-                        this._initializer(type, initializers, 'Weights', 'forgetGatePeepholeVector' + suffix, vectorShape, weights.forgetGatePeepholeVector);
-                        this._initializer(type, initializers, 'Weights', 'outputGatePeepholeVector' + suffix, vectorShape, weights.outputGatePeepholeVector);
+                        this._initializer(type, initializers, 'Weights', 'inputGatePeepholeVector' + suffix, [h], weights.inputGatePeepholeVector);
+                        this._initializer(type, initializers, 'Weights', 'forgetGatePeepholeVector' + suffix, [h], weights.forgetGatePeepholeVector);
+                        this._initializer(type, initializers, 'Weights', 'outputGatePeepholeVector' + suffix, [h], weights.outputGatePeepholeVector);
                     }
                 }
                 return { 'weightParams': true };
@@ -1244,110 +1244,15 @@ coreml.Tensor = class {
         return null;
     }
 
-    get state() {
-        return this._context().state;
-    }
-
-    get value() {
-        const context = this._context();
-        if (context.state) {
-            return null;
-        }
-        context.limit = Number.MAX_SAFE_INTEGER;
-        return this._decode(context, 0);
-    }
-
-    toString() {
-        const context = this._context();
-        if (context.state) {
-            return '';
-        }
-        context.limit = 10000;
-        const value = this._decode(context, 0);
-        return JSON.stringify(value, null, 4);
-    }
-
-    _context() {
-        const context = {};
-        context.state = null;
-        context.index = 0;
-        context.count = 0;
-        context.dataType = this._type.dataType;
-        context.dimensions = this._type.shape.dimensions;
-
-        if (!this._data) {
-            context.state = 'Tensor data is empty.';
-            return context;
-        }
-
-
-        switch (context.dataType) {
-            case 'float32':
-                context.data = this._data;
-                break;
-            case 'float16':
-                context.data = new DataView(this._data.buffer, this._data.byteOffset, this._data.byteLength);
-                break;
-            case 'uint8':
-                context.data = new DataView(this._data.buffer, this._data.byteOffset, this._data.byteLength);
-                break;
-            default:
-                if (this._quantization) {
-                    context.dataType = 'quantization';
-                    context.bits = this._quantization.numberOfBits.toNumber();
-                    context.data = new DataView(this._data.buffer, this._data.byteOffset, this._data.byteLength);
-                }
-                else {
-                    context.state = 'Tensor data type is not implemented.';
-                }
-                break;
+    get layout() {
+        switch (this._type.dataType) {
+            case 'float32': return '|';
+            default: return '<';
         }
-
-        return context;
     }
 
-    _decode(context, dimension) {
-        const results = [];
-        const size = context.dimensions[dimension];
-        if (dimension == context.dimensions.length - 1) {
-            for (let i = 0; i < size; i++) {
-                if (context.count > context.limit) {
-                    results.push('...');
-                    return results;
-                }
-                switch (context.dataType) {
-                    case 'float32':
-                        results.push(this._data[context.index]);
-                        context.index++;
-                        break;
-                    case 'float16':
-                        results.push(context.data.getFloat16(context.index, true));
-                        context.index += 2;
-                        break;
-                    case 'uint8':
-                        results.push(context.data.getUint8(context.index, true));
-                        context.index += 1;
-                        break;
-                    case 'quantization':
-                        results.push(context.data.getBits(context.index, context.bits));
-                        context.index++;
-                        break;
-                    default:
-                        break;
-                }
-                context.count++;
-            }
-        }
-        else {
-            for (let j = 0; j < size; j++) {
-                if (context.count > context.limit) {
-                    results.push('...');
-                    return results;
-                }
-                results.push(this._decode(context, dimension + 1));
-            }
-        }
-        return results;
+    get values() {
+        return this._data;
     }
 };
 

+ 1 - 1
source/dlc.js

@@ -363,7 +363,7 @@ dlc.Tensor = class {
         return this._type;
     }
 
-    get encoding() {
+    get layout() {
         return '|';
     }
 

+ 1 - 1
source/flax.js

@@ -218,7 +218,7 @@ flax.Tensor = class {
         return this._type;
     }
 
-    get encoding() {
+    get layout() {
         switch (this._type.dataType) {
             case 'string':
             case 'object':

+ 1 - 1
source/keras.js

@@ -1043,7 +1043,7 @@ keras.Tensor = class {
         return this._type;
     }
 
-    get encoding() {
+    get layout() {
         return this._littleEndian ? '<' : '>';
     }
 

+ 1 - 1
source/mnn.js

@@ -382,7 +382,7 @@ mnn.Tensor = class {
         return this._type;
     }
 
-    get encoding() {
+    get layout() {
         switch (this._type.dataType) {
             case 'int32':
             case 'float32':

+ 1 - 1
source/mslite.js

@@ -320,7 +320,7 @@ mslite.Tensor = class {
         return this._type;
     }
 
-    get encoding() {
+    get layout() {
         switch (this._type.dataType) {
             case 'string': return '|';
             default: return '<';

+ 1 - 1
source/nnabla.js

@@ -331,7 +331,7 @@ nnabla.Tensor = class {
         return this._type;
     }
 
-    get encoding() {
+    get layout() {
         return '|';
     }
 

+ 1 - 1
source/numpy.js

@@ -276,7 +276,7 @@ numpy.Tensor = class  {
         return this._type;
     }
 
-    get encoding() {
+    get layout() {
         switch (this._type.dataType) {
             case 'string':
             case 'object':

+ 48 - 289
source/onnx.js

@@ -806,8 +806,19 @@ onnx.Tensor = class {
 
     constructor(context, tensor, category) {
         this._category = category || null;
-        const data = (tensor) => {
-            let data = undefined;
+        if ((onnx.proto && tensor instanceof onnx.proto.SparseTensorProto) ||
+            (onnx.schema && tensor instanceof onnx.schema.SparseTensor)) {
+            this._name = tensor.values.name || '';
+            this._type = context.createTensorType(tensor.values.data_type, tensor.dims.map((dim) => dim), null);
+            this._location = Array.from(new Set([ context.createLocation(tensor.values.data_location), context.createLocation(tensor.indices.data_location) ])).join(':');
+            this._layout = 'sparse';
+            this._values = new onnx.Tensor(context, tensor.values);
+            this._indices = new onnx.Tensor(context, tensor.indices);
+        }
+        else {
+            this._name = tensor.name || '';
+            this._type = context.createTensorType(tensor.data_type, tensor.dims.map((dim) => dim), null);
+            this._location = context.createLocation(tensor.data_location);
             if (tensor.data_location === onnx.DataLocation.DEFAULT) {
                 switch (tensor.data_type) {
                     case onnx.DataType.FLOAT16:
@@ -818,51 +829,60 @@ onnx.Tensor = class {
                             for (let i = 0; i < array.length; i++) {
                                 view.setUint16(i << 1, array[i], true);
                             }
-                            data = {
-                                type: tensor.data_type,
-                                buffer: buffer
-                            };
+                            this._data = buffer;
+                            this._layout = '<';
                         }
                         break;
                     case onnx.DataType.FLOAT:
-                        data = new Float32Array(tensor.float_data);
+                        this._data = new Float32Array(tensor.float_data);
+                        this._layout = '|';
                         break;
                     case onnx.DataType.DOUBLE:
-                        data = new Float64Array(tensor.double_data);
+                        this._data = new Float64Array(tensor.double_data);
+                        this._layout = '|';
                         break;
                     case onnx.DataType.BOOL:
                         if (tensor.int32_data && tensor.int32_data.length > 0) {
                             const array = tensor.int32_data;
-                            data = new Array(array.length);
-                            for (let i = 0; i < data.length; i++) {
-                                data[i] = array[i] === 0 ? false : true;
+                            this._data = new Array(array.length);
+                            for (let i = 0; i < this._data.length; i++) {
+                                this._data[i] = array[i] === 0 ? false : true;
                             }
+                            this._layout = '|';
                         }
                         break;
                     case onnx.DataType.INT8:
-                        data = new Int8Array(tensor.int32_data);
+                        this._data = new Int8Array(tensor.int32_data);
+                        this._layout = '|';
                         break;
                     case onnx.DataType.UINT8:
-                        data = new Uint8Array(tensor.int32_data);
+                        this._data = new Uint8Array(tensor.int32_data);
+                        this._layout = '|';
                         break;
                     case onnx.DataType.INT16:
-                        data = new Int32Array(tensor.int32_data);
+                        this._data = new Int32Array(tensor.int32_data);
+                        this._layout = '|';
                         break;
                     case onnx.DataType.UINT16:
-                        data = new Int32Array(tensor.int32_data);
+                        this._data = new Int32Array(tensor.int32_data);
+                        this._layout = '|';
                         break;
                     case onnx.DataType.INT32:
-                        data = new Int32Array(tensor.int32_data);
+                        this._data = new Int32Array(tensor.int32_data);
+                        this._layout = '|';
                         break;
                     case onnx.DataType.UINT32:
                     case onnx.DataType.UINT64:
-                        data = tensor.uint64_data;
+                        this._data = tensor.uint64_data;
+                        this._layout = '|';
                         break;
                     case onnx.DataType.INT64:
-                        data = tensor.int64_data;
+                        this._data = tensor.int64_data;
+                        this._layout = '|';
                         break;
                     case onnx.DataType.STRING:
-                        data = tensor.string_data;
+                        this._data = tensor.string_data;
+                        this._layout = '|';
                         break;
                     case onnx.DataType.BFLOAT16:
                     case onnx.DataType.COMPLEX64:
@@ -871,32 +891,14 @@ onnx.Tensor = class {
                     default:
                         throw new onnx.Error("Unsupported tensor data type '" + tensor.data_type + "'.");
                 }
-                if (data && (Array.isArray(data) || ArrayBuffer.isView(data)) && data.length === 0) {
-                    data = undefined;
+                if (this._data && (Array.isArray(this._data) || ArrayBuffer.isView(this._data)) && this._data.length === 0) {
+                    this._data = undefined;
                 }
-                if (!data && tensor.raw_data && tensor.raw_data.length > 0) {
-                    data = {
-                        type: tensor.data_type,
-                        buffer: tensor.raw_data
-                    };
+                if (!this._data && tensor.raw_data && tensor.raw_data.length > 0) {
+                    this._data = tensor.raw_data;
+                    this._layout = '<';
                 }
             }
-            return data;
-        };
-        if ((onnx.proto && tensor instanceof onnx.proto.SparseTensorProto) ||
-            (onnx.schema && tensor instanceof onnx.schema.SparseTensor)) {
-            this._name = tensor.values.name || '';
-            this._type = context.createTensorType(tensor.values.data_type, tensor.dims.map((dim) => dim), null);
-            this._location = Array.from(new Set([ context.createLocation(tensor.values.data_location), context.createLocation(tensor.indices.data_location) ])).join(':');
-            this._layout = 'sparse';
-            this._values = data(tensor.values);
-            this._indices = data(tensor.indices);
-        }
-        else {
-            this._name = tensor.name || '';
-            this._type = context.createTensorType(tensor.data_type, tensor.dims.map((dim) => dim), null);
-            this._location = context.createLocation(tensor.data_location);
-            this._values = data(tensor);
         }
     }
 
@@ -916,257 +918,14 @@ onnx.Tensor = class {
         return this._type;
     }
 
-    get state() {
-        return this._context().state || null;
+    get indices() {
+        return this._indices;
     }
 
-    get value() {
-        const context = this._context();
-        if (context.state) {
-            return null;
-        }
-        context.limit = Number.MAX_SAFE_INTEGER;
-        return this._decode(context, 0);
+    get values() {
+        return this._layout === 'sparse' ? this._values : this._data;
     }
 
-    toString() {
-        const context = this._context();
-        if (context.state) {
-            return '';
-        }
-        context.limit = 10000;
-        const value = this._decode(context, 0);
-        return onnx.Tensor._stringify(value, '', '    ');
-    }
-
-    _context() {
-        const context = {};
-        context.state = null;
-        if (this._sparse) {
-            context.state = 'Sparse data not implemented.';
-            return context;
-        }
-        if (this._location !== 'default') {
-            context.state = "Data '" + this._location + "' location not implemented.";
-            return context;
-        }
-        const decode = (data) => {
-            if (!data || Array.isArray(data) || ArrayBuffer.isView(data)) {
-                return data;
-            }
-            const buffer = data.buffer;
-            const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
-            const type = data.type;
-            data = undefined;
-            switch (type) {
-                case onnx.DataType.BOOL:
-                    data = new Array(buffer.length);
-                    for (let i = 0; i < buffer.length; i++) {
-                        data[i] = view.getUint8(i) === 0 ? false : true;
-                    }
-                    break;
-                case onnx.DataType.FLOAT16:
-                    data = new Float32Array(buffer.length >> 1);
-                    for (let i = 0; i < data.length; i++) {
-                        data[i] = view.getFloat16(i << 1, true);
-                    }
-                    break;
-                case onnx.DataType.FLOAT:
-                    data = new Float32Array(buffer.length >> 2);
-                    for (let i = 0; i < data.length; i++) {
-                        data[i] = view.getFloat32(i << 2, true);
-                    }
-                    break;
-                case onnx.DataType.DOUBLE:
-                    data = new Float64Array(buffer.length >> 3);
-                    for (let i = 0; i < data.length; i++) {
-                        data[i] = view.getFloat64(i << 3, true);
-                    }
-                    break;
-                case onnx.DataType.INT8:
-                    data = new Int8Array(buffer.length);
-                    for (let i = 0; i < data.length; i++) {
-                        data[i] = view.getInt8(i, true);
-                    }
-                    break;
-                case onnx.DataType.UINT8:
-                    data = new Uint8Array(buffer.length);
-                    for (let i = 0; i < data.length; i++) {
-                        data[i] = view.getUint8(i, true);
-                    }
-                    break;
-                case onnx.DataType.INT16:
-                    data = new Int16Array(buffer.length >> 1);
-                    for (let i = 0; i < data.length; i++) {
-                        data[i] = view.getInt16(i << 1, true);
-                    }
-                    break;
-                case onnx.DataType.UINT16:
-                    data = new Uint16Array(buffer.length >> 1);
-                    for (let i = 0; i < data.length; i++) {
-                        data[i] = view.getUint16(i << 1, true);
-                    }
-                    break;
-                case onnx.DataType.INT32:
-                    data = new Int32Array(buffer.length >> 2);
-                    for (let i = 0; i < data.length; i++) {
-                        data[i] = view.getInt32(i << 2, true);
-                    }
-                    break;
-                case onnx.DataType.UINT32:
-                    data = new Uint32Array(buffer.length >> 2);
-                    for (let i = 0; i < data.length; i++) {
-                        data[i] = view.getUint32(i << 2, true);
-                    }
-                    break;
-                case onnx.DataType.INT64:
-                    data = new Array(buffer.length >> 3);
-                    for (let i = 0; i < data.length; i++) {
-                        data[i] = view.getInt64(i << 3, true);
-                    }
-                    break;
-                case onnx.DataType.UINT64:
-                    data = new Array(buffer.length >> 3);
-                    for (let i = 0; i < data.length; i++) {
-                        data[i] = view.getUint64(i << 3, true);
-                    }
-                    break;
-                case onnx.DataType.BFLOAT16:
-                    data = new Array(buffer.length >> 1);
-                    for (let i = 0; i < data.length; i++) {
-                        data[i] = view.getBfloat16(i << 1, true);
-                    }
-                    break;
-                case onnx.DataType.COMPLEX64:
-                    data = new Array(buffer.length >> 3);
-                    for (let i = 0; i < data.length; i++) {
-                        data[i] = view.getComplex64(i << 3, true);
-                    }
-                    break;
-                case onnx.DataType.COMPLEX128:
-                    data = new Array(buffer.length >> 4);
-                    for (let i = 0; i < data.length; i++) {
-                        data[i] = view.getComplex64(i << 4, true);
-                    }
-                    break;
-                default:
-                    throw new onnx.Error("Unsupported tensor data type '" + type + "'.");
-            }
-            return data;
-        };
-        this._values = decode(this._values);
-        if (!this._values) {
-            context.state = 'Tensor data is empty.';
-            return context;
-        }
-        this._indices = decode(this._indices);
-        context.values = this._values;
-        context.indices = this._indices;
-        context.index = 0;
-        context.dataType = this.type.dataType;
-        context.shape = this.type.shape.dimensions;
-        context.data = function() {
-            if (!this._data) {
-                if (this.indices && this.values && this.indices.length === this.values.length) {
-                    const size = context.shape.reduce((a, b) => a * b, 1);
-                    const indices = this.indices;
-                    const values = this.values;
-                    const array = new values.constructor(size);
-                    switch (this.dataType) {
-                        case 'boolean':
-                            array.fill(false);
-                            break;
-                        case 'int64':
-                        case 'uint64':
-                            break;
-                        default:
-                            break;
-                    }
-                    if (indices.length > 0) {
-                        if (Object.prototype.hasOwnProperty.call(indices[0], 'low')) {
-                            for (let i = 0; i < indices.length; i++) {
-                                const index = indices[i];
-                                array[index.high === 0 ? index.low : index.toNumber()] = values[i];
-                            }
-                        }
-                        else {
-                            for (let i = 0; i < indices.length; i++) {
-                                array[indices[i]] = values[i];
-                            }
-                        }
-                    }
-                    this._data = array;
-                }
-                else {
-                    this._data = this.values;
-                }
-            }
-            return this._data;
-        };
-        return context;
-    }
-
-    _decode(context, dimension) {
-        const shape = context.shape.length !== 0 ? context.shape : [ 1 ];
-        const results = [];
-        const size = shape[dimension];
-        const data = context.data();
-        if (dimension == shape.length - 1) {
-            for (let i = 0; i < size; i++) {
-                if (context.index > context.limit) {
-                    results.push('...');
-                    return results;
-                }
-                results.push(data[context.index++]);
-            }
-        }
-        else {
-            for (let j = 0; j < size; j++) {
-                if (context.index > context.limit) {
-                    results.push('...');
-                    return results;
-                }
-                results.push(this._decode(context, dimension + 1));
-            }
-        }
-        if (context.shape.length == 0) {
-            return results[0];
-        }
-        return results;
-    }
-
-    static _stringify(value, indentation, indent) {
-        if (Array.isArray(value)) {
-            const result = [];
-            result.push(indentation + '[');
-            const items = value.map((item) => onnx.Tensor._stringify(item, indentation + indent, indent));
-            if (items.length > 0) {
-                result.push(items.join(',\n'));
-            }
-            result.push(indentation + ']');
-            return result.join('\n');
-        }
-        switch (typeof value) {
-            case 'string':
-                return indentation + value;
-            case 'number':
-                if (value == Infinity) {
-                    return indentation + 'Infinity';
-                }
-                if (value == -Infinity) {
-                    return indentation + '-Infinity';
-                }
-                if (isNaN(value)) {
-                    return indentation + 'NaN';
-                }
-                return indentation + value.toString();
-            default:
-                if (value && value.toString) {
-                    return indentation + value.toString();
-                }
-                return indentation + '(undefined)';
-        }
-    }
 };
 
 onnx.TensorType = class {

+ 1 - 1
source/python.js

@@ -4759,7 +4759,7 @@ python.Execution = class {
                 throw new python.Error("Unsupported values in layout'" + this._layout.__str__() + "'.");
             }
             get indices() {
-                if (this._indices === torch.sparse_coo) {
+                if (this._layout === torch.sparse_coo) {
                     return this._indices;
                 }
                 throw new python.Error("Unsupported indices in layout'" + this._indices.__str__() + "'.");

+ 90 - 29
source/pytorch.js

@@ -65,7 +65,7 @@ pytorch.Graph = class {
                 if (graph.constants) {
                     for (const constant of graph.constants) {
                         if (pytorch.Utility.isTensor(constant)) {
-                            constant.initializer = pytorch.Utility.createTensor(constant.__variable__, constant, this._littleEndian);
+                            constant.initializer = new pytorch.Tensor(constant.__variable__, constant, this._littleEndian);
                             initializers.set(constant.__variable__, constant);
                         }
                         else if (constant && constant.__class__ && constant.__class__.__module__ && constant.__class__.__name__) {
@@ -78,7 +78,7 @@ pytorch.Graph = class {
                                     for (const key of Object.keys(constant)) {
                                         const value = constant[key];
                                         if (pytorch.Utility.isTensor(value)) {
-                                            value.initializer = pytorch.Utility.createTensor(value.__variable__, value, this._littleEndian);
+                                            value.initializer = new pytorch.Tensor(value.__variable__, value, this._littleEndian);
                                             initializers.set(value.__variable__, value);
                                         }
                                     }
@@ -107,7 +107,7 @@ pytorch.Graph = class {
                                         const parameter = obj;
                                         parameter.__parent__ = module;
                                         if (!parameter.initializer && parameter.storage()) {
-                                            parameter.initializer = pytorch.Utility.createTensor(parameter.name, parameter, this._littleEndian);
+                                            parameter.initializer = new pytorch.Tensor(parameter.name, parameter, this._littleEndian);
                                         }
                                         if (parameter.__variable__ && parameter.__count__ === 1) {
                                             initializers.set(parameter.__variable__, parameter);
@@ -166,7 +166,7 @@ pytorch.Graph = class {
                     const inputs = state_group.states.map((parameter) => {
                         return new pytorch.Parameter(parameter.name, true,
                             parameter.arguments.map((state) => {
-                                const tensor = pytorch.Utility.createTensor(state.id, pytorch.Utility.toTensor(state.value), this._littleEndian);
+                                const tensor = new pytorch.Tensor(state.id, pytorch.Utility.toTensor(state.value), this._littleEndian);
                                 return new pytorch.Argument(state.id, null, tensor);
                             }));
                     });
@@ -251,7 +251,7 @@ pytorch.Graph = class {
                 visible = input.visible === false ? false : true;
             }
             if (value) {
-                const initializer = pytorch.Utility.createTensor('', value, this._littleEndian);
+                const initializer = new pytorch.Tensor('', value, this._littleEndian);
                 inputs.push(new pytorch.Parameter(inputName || key, visible, [ new pytorch.Argument('', null, initializer) ]));
             }
         }
@@ -614,13 +614,27 @@ pytorch.Attribute = class {
 
 pytorch.Tensor = class {
 
-    constructor(name, type, layout, stride, data, littleEndian) {
+    constructor(name, tensor, littleEndian) {
         this._name = name || '';
-        this._type = type;
-        this._layout = (layout || '').split('.').pop().replace('_', '.');
-        this._stride = stride;
-        this._data = data;
         this._littleEndian = littleEndian;
+        const storage = tensor.storage();
+        const size = tensor.size();
+        this._type = new pytorch.TensorType(storage.dtype.__reduce__(), new pytorch.TensorShape(size));
+        const layout = tensor.layout ? tensor.layout.__str__() : null;
+        this._stride = tensor.stride();
+        if (layout && layout.startsWith('torch.sparse_')) {
+            this._layout = layout.split('.').pop().replace('_', '.');
+            this._indices = new pytorch.Tensor('', tensor.indices, littleEndian);
+            this._values = new pytorch.Tensor('', tensor.values, littleEndian);
+        }
+        else if (!layout || layout === 'torch.strided') {
+            this._layout = this._littleEndian ? '<' : '>';
+            this._indices = null;
+            this._data = storage.data;
+        }
+        else {
+            throw new pytorch.Error("Unsupported tensor layout '" + layout + "'.");
+        }
     }
 
     get name() {
@@ -639,11 +653,14 @@ pytorch.Tensor = class {
         return this._stride;
     }
 
-    get encoding() {
-        return this._littleEndian ? '<' : '>';
+    get indices() {
+        return this._indices;
     }
 
     get values() {
+        if (this._layout && this._layout.startsWith('sparse.')) {
+            return this._values;
+        }
         return this._data instanceof Uint8Array ? this._data : this._data.peek();
     }
 };
@@ -733,7 +750,7 @@ pytorch.Execution = class extends python.Execution {
                 }
                 const tensors = state[1];
                 const opt_tensors = state[2];
-                const packed_config_tensor = pytorch.Utility.createTensor('', tensors[0], true);
+                const packed_config_tensor = new pytorch.Tensor('', tensors[0], true);
                 const packed_config = pytorch.Utility.values(packed_config_tensor);
                 this.weight = tensors[1];
                 this.bias = opt_tensors[0];
@@ -752,7 +769,7 @@ pytorch.Execution = class extends python.Execution {
                 }
                 const tensors = state[1];
                 const opt_tensors = state[2];
-                const packed_config_tensor = pytorch.Utility.createTensor('', tensors[0], true);
+                const packed_config_tensor = new pytorch.Tensor('', tensors[0], true);
                 const packed_config = pytorch.Utility.values(packed_config_tensor);
                 this.weight = tensors[1];
                 this.bias = opt_tensors[0];
@@ -2555,7 +2572,7 @@ pytorch.Utility = class {
         if (type && data) {
             switch (type.dataType) {
                 case 'int16': {
-                    if (tensor.encoding === '<') {
+                    if (tensor.layout === '<') {
                         return new Uint16Array(data);
                     }
                     break;
@@ -2594,14 +2611,6 @@ pytorch.Utility = class {
         }
     }
 
-    static createTensor(name, tensor, littleEndian) {
-        const storage = tensor.storage();
-        const size = tensor.size();
-        const type = new pytorch.TensorType(storage.dtype.__reduce__(), new pytorch.TensorShape(size));
-        const layout = tensor.layout ? tensor.layout.__str__() : null;
-        return new pytorch.Tensor(name || '', type, layout, tensor.stride(), storage.data, littleEndian);
-    }
-
     static getType(value) {
         if (pytorch.Utility.isTensor(value)) {
             return 'Tensor';
@@ -3363,10 +3372,35 @@ pytorch.nnapi.Argument = class {
     constructor(operand) {
         this._name = operand.index.toString();
         const shape = new pytorch.TensorShape(operand.dimensions);
-        this._type = new pytorch.TensorType(operand.data_type.replace('[]', ''), shape);
-        this._initializer = operand.data ? new pytorch.Tensor(this._name, this._type, null, null, operand.data, true) : null;
-        this._scale = operand.scale;
-        this._zeroPoint = operand.zero_point;
+        let dataType = operand.data_type.replace('[]', '');
+        let quantizationType = null;
+        switch (dataType) {
+            case 'quant8_asymm':
+            case 'quant8_symm_per_channel':
+            case 'quant8_symm':
+            case 'quant8_asymm_signed[]':
+            case 'quant16_asymm':
+            case 'quant16_symm':
+                quantizationType = dataType;
+                dataType = dataType.indexOf('16') !== -1 ? 'uint16' : 'uint8';
+                break;
+            default:
+                break;
+        }
+        this._type = new pytorch.TensorType(dataType, shape);
+        this._initializer = operand.data ? new pytorch.nnapi.Tensor(this._type, operand.data) : null;
+        if (quantizationType || operand.scale !== undefined || operand.zero_point !== undefined) {
+            this._quantization = {};
+            if (quantizationType) {
+                this._quantization.type = quantizationType;
+            }
+            if (operand.scale !== undefined) {
+                this._quantization.scale = operand.scale;
+            }
+            if (operand.zero_point !== undefined) {
+                this._quantization.zeroPoint = operand.zero_point;
+            }
+        }
     }
 
     get name() {
@@ -3378,8 +3412,15 @@ pytorch.nnapi.Argument = class {
     }
 
     get quantization() {
-        if (this._scale != 0 || this._zeroPoint != 0) {
-            return this._scale.toString() + ' * ' + (this._zeroPoint == 0 ? 'q' : ('(q - ' + this._zeroPoint.toString() + ')'));
+        if (this._quantization) {
+            let value = '';
+            if (this._quantization.scale != 0 || this._quantization.zeroPoint != 0) {
+                value = this._quantization.scale.toString() + ' * ' + (this._quantization.zeroPoint == 0 ? 'q' : ('(q - ' + this._quantization.zeroPoint.toString() + ')'));
+            }
+            if (this._quantization.type) {
+                return this._quantization.type + '(' + value + ')';
+            }
+            return value;
         }
         return null;
     }
@@ -3488,6 +3529,26 @@ pytorch.nnapi.Attribute = class {
     }
 };
 
+pytorch.nnapi.Tensor = class {
+
+    constructor(type, data) {
+        this._type = type;
+        this._data = data;
+    }
+
+    get type() {
+        return this._type;
+    }
+
+    get layout() {
+        return '<';
+    }
+
+    get values() {
+        return this._data;
+    }
+};
+
 pytorch.Metadata = class {
 
     static open(context) {

+ 1 - 1
source/sklearn.js

@@ -353,7 +353,7 @@ sklearn.Tensor = class {
         return 'NumPy Array';
     }
 
-    get encoding() {
+    get layout() {
         switch (this._type.dataType) {
             case 'string':
             case 'object':

+ 21 - 11
source/tf.js

@@ -1239,35 +1239,40 @@ tf.Tensor = class {
             this._type = new tf.TensorType(tensor.dtype, tensor.tensor_shape || tensor.tensorShape);
             this._tensor = tensor;
             if (Object.prototype.hasOwnProperty.call(tensor, 'tensor_content')) {
-                this._buffer = tensor.tensor_content;
+                this._values = tensor.tensor_content;
+                this._layout = '<';
             }
             else {
                 const DataType = tf.proto.tensorflow.DataType;
                 switch (tensor.dtype) {
                     case DataType.DT_BFLOAT16: {
                         const values = tensor.half_val || [];
-                        this._buffer = new Uint8Array(values.length << 2);
-                        const view = new DataView(this._buffer.buffer, this._buffer.byteOffset, this._buffer.byteLength);
+                        this._values = new Uint8Array(values.length << 2);
+                        const view = new DataView(this._values.buffer, this._values.byteOffset, this._values.byteLength);
                         for (let i = 0; i < values.length; i++) {
                             view.setUint32(i << 2, values[i] << 16, true);
                         }
+                        this._layout = '<';
                         break;
                     }
                     case DataType.DT_HALF: {
                         const values = tensor.half_val || [];
-                        this._buffer = new Uint8Array(values.length << 1);
-                        const view = new DataView(this._buffer.buffer, this._buffer.byteOffset, this._buffer.byteLength);
+                        this._values = new Uint8Array(values.length << 1);
+                        const view = new DataView(this._values.buffer, this._values.byteOffset, this._values.byteLength);
                         for (let i = 0; i < values.length; i++) {
                             view.setUint16(i << 1, values[i], true);
                         }
+                        this._layout = '<';
                         break;
                     }
                     case DataType.DT_FLOAT: {
                         this._values = tensor.float_val || null;
+                        this._layout = '|';
                         break;
                     }
                     case DataType.DT_DOUBLE: {
                         this._values = tensor.double_val || null;
+                        this._layout = '|';
                         break;
                     }
                     case DataType.DT_UINT8:
@@ -1276,26 +1281,32 @@ tf.Tensor = class {
                     case DataType.DT_INT16:
                     case DataType.DT_INT32: {
                         this._values = tensor.int_val || null;
+                        this._layout = '|';
                         break;
                     }
                     case DataType.DT_UINT32: {
                         this._values = tensor.uint32_val || null;
+                        this._layout = '|';
                         break;
                     }
                     case DataType.DT_INT64: {
                         this._values = tensor.int64_val || null;
+                        this._layout = '|';
                         break;
                     }
                     case DataType.DT_UINT64: {
                         this._values = tensor.uint64_val || null;
+                        this._layout = '|';
                         break;
                     }
                     case DataType.DT_BOOL: {
                         this._values = tensor.bool_val || null;
+                        this._layout = '|';
                         break;
                     }
                     case DataType.DT_STRING: {
                         this._values = tensor.string_val || null;
+                        this._layout = '|';
                         break;
                     }
                     default: {
@@ -1322,13 +1333,13 @@ tf.Tensor = class {
         return this._category;
     }
 
-    get encoding() {
-        return Array.isArray(this._values) ? '|' : '<';
+    get layout() {
+        return this._layout;
     }
 
     get values() {
-        if (Array.isArray(this._values)) {
-            let values = this._values;
+        let values = this._values;
+        if (this._layout === '|' && Array.isArray(values)) {
             if (this._type.dataType === 'string') {
                 values = values.map((value) => tf.Utility.decodeText(value));
             }
@@ -1337,9 +1348,8 @@ tf.Tensor = class {
             if (values.length === 1 && size > 1) {
                 values = new Array(size).fill(values[0]);
             }
-            return values;
         }
-        return this._buffer;
+        return values;
     }
 };
 

+ 1 - 1
source/tflite.js

@@ -580,7 +580,7 @@ tflite.Tensor = class {
         return this._type;
     }
 
-    get encoding() {
+    get layout() {
         switch (this._type.dataType) {
             case 'string': return '|';
             default: return '<';

+ 1 - 1
source/torch.js

@@ -499,7 +499,7 @@ torch.Tensor = class {
         return this._type;
     }
 
-    get encoding() {
+    get layout() {
         return '|';
     }
 

+ 219 - 264
source/view-sidebar.js

@@ -122,9 +122,27 @@ sidebar.Sidebar = class {
     }
 };
 
-sidebar.NodeSidebar = class {
+sidebar.Control = class {
+
+    on(event, callback) {
+        this._events = this._events || {};
+        this._events[event] = this._events[event] || [];
+        this._events[event].push(callback);
+    }
+
+    _raise(event, data) {
+        if (this._events && this._events[event]) {
+            for (const callback of this._events[event]) {
+                callback(this, data);
+            }
+        }
+    }
+};
+
+sidebar.NodeSidebar = class extends sidebar.Control {
 
     constructor(host, node) {
+        super();
         this._host = host;
         this._node = node;
         this._elements = [];
@@ -255,20 +273,6 @@ sidebar.NodeSidebar = class {
             }
         }
     }
-
-    on(event, callback) {
-        this._events = this._events || {};
-        this._events[event] = this._events[event] || [];
-        this._events[event].push(callback);
-    }
-
-    _raise(event, data) {
-        if (this._events && this._events[event]) {
-            for (const callback of this._events[event]) {
-                callback(this, data);
-            }
-        }
-    }
 };
 
 sidebar.NameValueView = class {
@@ -314,9 +318,10 @@ sidebar.NameValueView = class {
     }
 };
 
-sidebar.SelectView = class {
+sidebar.SelectView = class extends sidebar.Control {
 
     constructor(host, values, selected) {
+        super();
         this._host = host;
         this._elements = [];
         this._values = values;
@@ -341,20 +346,6 @@ sidebar.SelectView = class {
     render() {
         return this._elements;
     }
-
-    on(event, callback) {
-        this._events = this._events || {};
-        this._events[event] = this._events[event] || [];
-        this._events[event].push(callback);
-    }
-
-    _raise(event, data) {
-        if (this._events && this._events[event]) {
-            for (const callback of this._events[event]) {
-                callback(this, data);
-            }
-        }
-    }
 };
 
 sidebar.ValueTextView = class {
@@ -395,9 +386,86 @@ sidebar.ValueTextView = class {
     }
 };
 
-sidebar.AttributeView = class {
+sidebar.ValueView = class extends sidebar.Control {
+
+    _bold(name, value) {
+        const line = this._host.document.createElement('div');
+        line.innerHTML = name + ': ' + '<b>' + value + '</b>';
+        this._add(line);
+    }
+
+    _code(name, value) {
+        const line = this._host.document.createElement('div');
+        line.innerHTML = name + ': ' + '<code><b>' + value + '</b></code>';
+        this._add(line);
+    }
+
+    _add(child) {
+        child.className = this._element.childNodes.length < 2 ? 'sidebar-view-item-value-line' : 'sidebar-view-item-value-line-border';
+        this._element.appendChild(child);
+    }
+
+    _tensor(value) {
+        const contentLine = this._host.document.createElement('pre');
+        try {
+            const tensor = new sidebar.Tensor(value);
+            const layout = tensor.layout;
+            if (layout) {
+                const layouts = new Map([
+                    [ 'sparse', 'Sparse' ],
+                    [ 'sparse.coo', 'Sparse COO' ],
+                    [ 'sparse.csr', 'Sparse CSR' ],
+                    [ 'sparse.csc', 'Sparse CSC' ],
+                    [ 'sparse.bsr', 'Sparse BSR' ],
+                    [ 'sparse.bsc', 'Sparse BSC' ]
+                ]);
+                if (layouts.has(layout)) {
+                    this._bold('layout', layouts.get(layout));
+                }
+            }
+            if (tensor.layout !== '<' && tensor.layout !== '>' && tensor.layout !== '|' && tensor.layout !== 'sparse') {
+                contentLine.innerHTML = "Tensor layout '" + tensor.layout + "' is not implemented.";
+            }
+            else if (tensor.empty) {
+                contentLine.innerHTML = 'Tensor data is empty.';
+            }
+            else if (tensor.type && tensor.type.dataType === '?') {
+                contentLine.innerHTML = 'Tensor data type is not defined.';
+            }
+            else if (tensor.type && !tensor.type.shape) {
+                contentLine.innerHTML = 'Tensor shape is not defined.';
+            }
+            else {
+                contentLine.innerHTML = tensor.toString();
+
+                if (this._host.save &&
+                    value.type.shape && value.type.shape.dimensions &&
+                    value.type.shape.dimensions.length > 0) {
+                    this._saveButton = this._host.document.createElement('div');
+                    this._saveButton.className = 'sidebar-view-item-value-expander';
+                    this._saveButton.innerHTML = '&#x1F4BE;';
+                    this._saveButton.addEventListener('click', () => {
+                        this._raise('export-tensor', tensor);
+                    });
+                    this._element.appendChild(this._saveButton);
+                }
+            }
+        }
+        catch (err) {
+            contentLine.innerHTML = err.toString();
+            this._raise('error', err);
+        }
+        const valueLine = this._host.document.createElement('div');
+        valueLine.className = 'sidebar-view-item-value-line-border';
+        valueLine.appendChild(contentLine);
+        this._element.appendChild(valueLine);
+    }
+};
+
+sidebar.AttributeView = class extends sidebar.ValueView {
 
     constructor(host, attribute) {
+        super();
         this._host = host;
         this._attribute = attribute;
         this._element = this._host.document.createElement('div');
@@ -476,13 +544,7 @@ sidebar.AttributeView = class {
             }
 
             if (this._attribute.type == 'tensor' && value) {
-                const state = value.state;
-                const valueLine = this._host.document.createElement('div');
-                valueLine.className = 'sidebar-view-item-value-line-border';
-                const contentLine = this._host.document.createElement('pre');
-                contentLine.innerHTML = state || value.toString();
-                valueLine.appendChild(contentLine);
-                this._element.appendChild(valueLine);
+                this._tensor(value);
             }
         }
         else {
@@ -492,25 +554,12 @@ sidebar.AttributeView = class {
             }
         }
     }
-
-    on(event, callback) {
-        this._events = this._events || {};
-        this._events[event] = this._events[event] || [];
-        this._events[event].push(callback);
-    }
-
-    _raise(event, data) {
-        if (this._events && this._events[event]) {
-            for (const callback of this._events[event]) {
-                callback(this, data);
-            }
-        }
-    }
 };
 
-sidebar.ParameterView = class {
+sidebar.ParameterView = class extends sidebar.Control {
 
     constructor(host, list) {
+        super();
         this._list = list;
         this._elements = [];
         this._items = [];
@@ -536,25 +585,12 @@ sidebar.ParameterView = class {
             item.toggle();
         }
     }
-
-    on(event, callback) {
-        this._events = this._events || {};
-        this._events[event] = this._events[event] || [];
-        this._events[event].push(callback);
-    }
-
-    _raise(event, data) {
-        if (this._events && this._events[event]) {
-            for (const callback of this._events[event]) {
-                callback(this, data);
-            }
-        }
-    }
 };
 
-sidebar.ArgumentView = class {
+sidebar.ArgumentView = class extends sidebar.ValueView {
 
     constructor(host, argument) {
+        super();
         this._host = host;
         this._argument = argument;
 
@@ -651,80 +687,7 @@ sidebar.ArgumentView = class {
                 }
 
                 if (initializer) {
-                    const contentLine = this._host.document.createElement('pre');
-                    try {
-                        const tensor = new sidebar.Tensor(initializer);
-                        const layout = tensor.layout;
-                        if (layout) {
-                            this._bold('layout', layout);
-                        }
-
-                        if (Object.prototype.hasOwnProperty.call(Object.getPrototypeOf(initializer), 'state')) {
-                            const state = initializer.state;
-                            if (state === null && this._host.save &&
-                                initializer.type.dataType && initializer.type.dataType != '?' &&
-                                initializer.type.shape && initializer.type.shape.dimensions &&
-                                initializer.type.shape.dimensions.length > 0) {
-                                this._saveButton = this._host.document.createElement('div');
-                                this._saveButton.className = 'sidebar-view-item-value-expander';
-                                this._saveButton.innerHTML = '&#x1F4BE;';
-                                this._saveButton.addEventListener('click', () => {
-                                    this._raise('export-tensor', initializer);
-                                });
-                                this._element.appendChild(this._saveButton);
-                            }
-                            contentLine.innerHTML = state || initializer.toString();
-                        }
-                        else {
-                            try {
-                                if (tensor.layout != '') {
-                                    contentLine.innerHTML = "Tensor layout '" + tensor.layout + "' is not implemented.";
-                                }
-                                else if (!tensor.format) {
-                                    contentLine.innerHTML = 'Tensor data not implemented.';
-                                }
-                                else if (tensor.empty) {
-                                    contentLine.innerHTML = 'Tensor data is empty.';
-                                }
-                                else if (tensor.type && tensor.type.dataType === '?') {
-                                    contentLine.innerHTML = 'Tensor data type is not defined.';
-                                }
-                                else if (tensor.type && tensor.type.dataType === 'int4') {
-                                    contentLine.innerHTML = "Tensor data type 'int4' is not supported.";
-                                }
-                                else if (tensor.type && !tensor.type.shape) {
-                                    contentLine.innerHTML = 'Tensor shape is not defined.';
-                                }
-                                else {
-                                    contentLine.innerHTML = tensor.toString();
-
-                                    if (this._host.save &&
-                                    initializer.type.shape && initializer.type.shape.dimensions &&
-                                    initializer.type.shape.dimensions.length > 0) {
-                                        this._saveButton = this._host.document.createElement('div');
-                                        this._saveButton.className = 'sidebar-view-item-value-expander';
-                                        this._saveButton.innerHTML = '&#x1F4BE;';
-                                        this._saveButton.addEventListener('click', () => {
-                                            this._raise('export-tensor', tensor);
-                                        });
-                                        this._element.appendChild(this._saveButton);
-                                    }
-                                }
-                            }
-                            catch (err) {
-                                contentLine.innerHTML = err.toString();
-                                this._raise('error', err);
-                            }
-                        }
-                    }
-                    catch (err) {
-                        contentLine.innerHTML = err.toString();
-                        this._raise('error', err);
-                    }
-                    const valueLine = this._host.document.createElement('div');
-                    valueLine.className = 'sidebar-view-item-value-line-border';
-                    valueLine.appendChild(contentLine);
-                    this._element.appendChild(valueLine);
+                    this._tensor(initializer);
                 }
             }
             else {
@@ -735,42 +698,12 @@ sidebar.ArgumentView = class {
             }
         }
     }
-
-    on(event, callback) {
-        this._events = this._events || {};
-        this._events[event] = this._events[event] || [];
-        this._events[event].push(callback);
-    }
-
-    _raise(event, data) {
-        if (this._events && this._events[event]) {
-            for (const callback of this._events[event]) {
-                callback(this, data);
-            }
-        }
-    }
-
-    _bold(name, value) {
-        const line = this._host.document.createElement('div');
-        line.innerHTML = name + ': ' + '<b>' + value + '</b>';
-        this._add(line);
-    }
-
-    _code(name, value) {
-        const line = this._host.document.createElement('div');
-        line.innerHTML = name + ': ' + '<code><b>' + value + '</b></code>';
-        this._add(line);
-    }
-
-    _add(child) {
-        child.className = this._element.childNodes.length < 2 ? 'sidebar-view-item-value-line' : 'sidebar-view-item-value-line-border';
-        this._element.appendChild(child);
-    }
 };
 
-sidebar.ModelSidebar = class {
+sidebar.ModelSidebar = class extends sidebar.Control {
 
     constructor(host, model, graph) {
+        super();
         this._host = host;
         this._model = model;
         this._elements = [];
@@ -867,25 +800,12 @@ sidebar.ModelSidebar = class {
         const item = new sidebar.NameValueView(this._host, name, view);
         this._elements.push(item.render());
     }
-
-    on(event, callback) {
-        this._events = this._events || {};
-        this._events[event] = this._events[event] || [];
-        this._events[event].push(callback);
-    }
-
-    _raise(event, data) {
-        if (this._events && this._events[event]) {
-            for (const callback of this._events[event]) {
-                callback(this, data);
-            }
-        }
-    }
 };
 
-sidebar.DocumentationSidebar = class {
+sidebar.DocumentationSidebar = class extends sidebar.Control {
 
     constructor(host, metadata) {
+        super();
         this._host = host;
         this._metadata = metadata;
     }
@@ -988,20 +908,6 @@ sidebar.DocumentationSidebar = class {
         return this._elements;
     }
 
-    on(event, callback) {
-        this._events = this._events || {};
-        this._events[event] = this._events[event] || [];
-        this._events[event].push(callback);
-    }
-
-    _raise(event, data) {
-        if (this._events && this._events[event]) {
-            for (const callback of this._events[event]) {
-                callback(this, data);
-            }
-        }
-    }
-
     _append(parent, type, content) {
         const element = this._host.document.createElement(type);
         if (content) {
@@ -1197,9 +1103,10 @@ sidebar.DocumentationSidebar = class {
     }
 };
 
-sidebar.FindSidebar = class {
+sidebar.FindSidebar =- class extends sidebar.Control {
 
     constructor(host, element, graph) {
+        super();
         this._host = host;
         this._graphElement = element;
         this._graph = graph;
@@ -1407,47 +1314,38 @@ sidebar.Tensor = class {
     constructor(tensor) {
         this._tensor = tensor;
         this._type = tensor.type;
-        this._layout = tensor.layout;
         this._stride = tensor.stride;
-        switch (tensor.encoding) {
+        switch (tensor.layout) {
             case undefined:
             case '':
             case '<': {
-                if (!this._layout || this._layout === 'strided') {
-                    this._data = this._tensor.values;
-                    if (this._data !== null && this._data !== undefined) {
-                        this._format = 1;
-                        this._littleEndian = true;
-                    }
-                }
+                this._data = this._tensor.values;
+                this._layout = '<';
+                this._littleEndian = true;
                 break;
             }
             case '>': {
-                if (!this._layout || this._layout === 'strided') {
-                    this._data = this._tensor.values;
-                    if (this._data !== null && this._data !== undefined) {
-                        this._format = 1;
-                        this._littleEndian = false;
-                    }
-                }
+                this._data = this._tensor.values;
+                this._layout = '>';
+                this._littleEndian = false;
                 break;
             }
             case '|': {
-                if (!this._layout || this._layout === 'strided') {
-                    this._values = this._tensor.values;
-                    if (this._values !== null && this._values !== undefined) {
-                        this._format = 2;
-                    }
-                }
+                this._values = this._tensor.values;
+                this._layout = '|';
+                break;
+            }
+            case 'sparse': {
+                this._indices = this._tensor.indices;
+                this._values = this._tensor.values;
+                this._layout = 'sparse';
                 break;
             }
             default: {
-                throw new Error("Unsupported tensor encoding '" + tensor.encoding + "'.");
+                this._layout = tensor.layout;
+                break;
             }
         }
-        if (this._format === undefined) {
-            this._format = 0;
-        }
         sidebar.Tensor.dataTypes = sidebar.Tensor.dataTypeSizes || new Map([
             [ 'boolean', 1 ],
             [ 'qint8', 1 ], [ 'qint16', 2 ], [ 'qint32', 4 ],
@@ -1464,38 +1362,40 @@ sidebar.Tensor = class {
     }
 
     get layout() {
-        switch (this._layout) {
-            case 'sparse': return 'Sparse';
-            case 'sparse.coo': return 'Sparse COO';
-            case 'sparse.csr': return 'Sparse CSR';
-            case 'sparse.csc': return 'Sparse CSC';
-            case 'sparse.bsr': return 'Sparse BSR';
-            case 'sparse.bsc': return 'Sparse BSC';
-            default: return '';
-        }
+        return this._layout;
     }
 
     get stride() {
         return this._stride;
     }
 
-    get format() {
-        return this._format;
-    }
-
     get empty() {
-        return (!(Array.isArray(this._data) || this._data instanceof Uint8Array || this._data instanceof Int8Array) || this._data.length === 0) &&
-               (!(Array.isArray(this._values) || ArrayBuffer.isView(this._values)) || this._values.length === 0);
+        switch (this._layout) {
+            case '<':
+            case '>': {
+                return !(Array.isArray(this._data) || this._data instanceof Uint8Array || this._data instanceof Int8Array) || this._data.length === 0;
+            }
+            case '|': {
+                return !(Array.isArray(this._values) || ArrayBuffer.isView(this._values)) || this._values.length === 0;
+            }
+            case 'sparse': {
+                return !this._values || this.indices || this._values.values.length === 0;
+            }
+            default: {
+                throw new Error("Unsupported tensor format '" + this._format + "'.");
+            }
+        }
     }
 
     get value() {
         const context = this._context();
         context.limit = Number.MAX_SAFE_INTEGER;
-        switch (this._format) {
-            case 1: {
+        switch (context.layout) {
+            case '<':
+            case '>': {
                 return this._decodeData(context, 0);
             }
-            case 2: {
+            case '|': {
                 return this._decodeValues(context, 0);
             }
             default: {
@@ -1507,12 +1407,13 @@ sidebar.Tensor = class {
     toString() {
         const context = this._context();
         context.limit = 10000;
-        switch (this._format) {
-            case 1: {
+        switch (context.layout) {
+            case '<':
+            case '>': {
                 const value = this._decodeData(context, 0);
                 return sidebar.Tensor._stringify(value, '', '    ');
             }
-            case 2: {
+            case '|': {
                 const value = this._decodeValues(context, 0);
                 return sidebar.Tensor._stringify(value, '', '    ');
             }
@@ -1523,31 +1424,42 @@ sidebar.Tensor = class {
     }
 
     _context() {
-        const context = {};
-        if (this._layout && this._layout !== 'strided') {
+        if (this._layout !== '<' && this._layout !== '>' && this._layout !== '|' && this._layout !== 'sparse') {
             throw new Error("Tensor layout '" + this._layout + "' is not supported.");
         }
+        const context = {};
+        context.layout = this._layout;
+        context.dimensions = this._type.shape.dimensions;
         const dataType = this._type.dataType;
         const size = this._type.shape.dimensions.reduce((a, b) => a * b, 1);
-        switch (this._format) {
-            case 1: {
+        switch (this._layout) {
+            case '<':
+            case '>': {
                 context.data = (this._data instanceof Uint8Array || this._data instanceof Int8Array) ? this._data : this._data.peek();
                 context.dataType = dataType;
-                context.dimensions = this._type.shape.dimensions;
                 context.view = new DataView(context.data.buffer, context.data.byteOffset, context.data.byteLength);
-                if (!sidebar.Tensor.dataTypes.has(dataType)) {
-                    throw new Error("Tensor data type '" + dataType + "' is not implemented.");
+                if (sidebar.Tensor.dataTypes.has(dataType)) {
+                    const itemsize = sidebar.Tensor.dataTypes.get(dataType);
+                    if (this._data.length < (itemsize * size)) {
+                        throw new Error('Invalid tensor data size.');
+                    }
+                }
+                else if (dataType.startsWith('uint') && !isNaN(parseInt(dataType.substring(4), 10))) {
+                    context.dataType = 'uint';
+                    context.bits = parseInt(dataType.substring(4), 10);
                 }
-                const itemsize = sidebar.Tensor.dataTypes.get(dataType);
-                if (this._data.length < (itemsize * size)) {
-                    throw new Error('Invalid tensor data size.');
+                else if (dataType.startsWith('int') && !isNaN(parseInt(dataType.substring(3), 10))) {
+                    context.dataType = 'int';
+                    context.bits = parseInt(dataType.substring(3), 10);
+                }
+                else {
+                    throw new Error("Tensor data type '" + dataType + "' is not implemented.");
                 }
                 break;
             }
-            case 2: {
+            case '|': {
                 context.data = this._values;
                 context.dataType = dataType;
-                context.dimensions = this._type.shape.dimensions;
                 if (!sidebar.Tensor.dataTypes.has(dataType) && dataType !== 'string' && dataType !== 'object') {
                     throw new Error("Tensor data type '" + dataType + "' is not implemented.");
                 }
@@ -1556,8 +1468,41 @@ sidebar.Tensor = class {
                 }
                 break;
             }
+            case 'sparse': {
+                context.dataType = dataType;
+                const size = context.dimensions.reduce((a, b) => a * b, 1);
+                const indices = this._indices.values;
+                const values = this._values.values;
+                const array = new values.constructor(size);
+                switch (context.dataType) {
+                    case 'boolean':
+                        array.fill(false);
+                        break;
+                    case 'int64':
+                    case 'uint64':
+                        break;
+                    default:
+                        break;
+                }
+                if (indices.length > 0) {
+                    if (Object.prototype.hasOwnProperty.call(indices[0], 'low')) {
+                        for (let i = 0; i < indices.length; i++) {
+                            const index = indices[i];
+                            array[index.high === 0 ? index.low : index.toNumber()] = values[i];
+                        }
+                    }
+                    else {
+                        for (let i = 0; i < indices.length; i++) {
+                            array[indices[i]] = values[i];
+                        }
+                    }
+                }
+                context.layout = '|';
+                context.data = array;
+                break;
+            }
             default: {
-                throw new sidebar.Tensor("Unsupported tensor format '" + this._format + "'.");
+                throw new sidebar.Tensor("Unsupported tensor layout '" + this._layout + "'.");
             }
         }
         context.index = 0;
@@ -1606,6 +1551,11 @@ sidebar.Tensor = class {
                         context.index += 8;
                         context.count++;
                         break;
+                    case 'int':
+                        results.push(view.getIntBits(context.index, context.bits));
+                        context.index++;
+                        context.count++;
+                        break;
                     case 'quint8':
                     case 'uint8':
                         results.push(view.getUint8(context.index));
@@ -1629,6 +1579,11 @@ sidebar.Tensor = class {
                         context.index += 8;
                         context.count++;
                         break;
+                    case 'uint':
+                        results.push(view.getUintBits(context.index, context.bits));
+                        context.index++;
+                        context.count++;
+                        break;
                     case 'float16':
                         results.push(view.getFloat16(context.index, this._littleEndian));
                         context.index += 2;

+ 6 - 17
source/view.js

@@ -1066,24 +1066,13 @@ view.Node = class extends grapher.Node {
                     if (type.shape.dimensions.length === 0 && argument.initializer) {
                         try {
                             const initializer = argument.initializer;
-                            if (Object.prototype.hasOwnProperty.call(Object.getPrototypeOf(initializer), 'state')) {
-                                if (!argument.initializer.state) {
-                                    shape = initializer.toString();
-                                    if (shape && shape.length > 10) {
-                                        shape = shape.substring(0, 10) + '\u2026';
-                                    }
-                                    separator = ' = ';
-                                }
-                            }
-                            else {
-                                const tensor = new sidebar.Tensor(initializer);
-                                if (tensor.layout === '' && tensor.format && !tensor.empty && tensor.type.dataType !== '?') {
-                                    shape = tensor.toString();
-                                    if (shape && shape.length > 10) {
-                                        shape = shape.substring(0, 10) + '\u2026';
-                                    }
-                                    separator = ' = ';
+                            const tensor = new sidebar.Tensor(initializer);
+                            if ((tensor.layout === '<' || tensor.layout === '>' || tensor.layout === '|') && !tensor.empty && tensor.type.dataType !== '?') {
+                                shape = tensor.toString();
+                                if (shape && shape.length > 10) {
+                                    shape = shape.substring(0, 10) + '\u2026';
                                 }
+                                separator = ' = ';
                             }
                         }
                         catch (err) {

+ 14 - 26
test/models.js

@@ -670,35 +670,23 @@ const loadModel = (target, item) => {
                             const log = (/* message */) => {
                                 // console.log('  ' + message);
                             };
-                            if (!Object.prototype.hasOwnProperty.call(Object.getPrototypeOf(argument.initializer), 'state')) {
-                                const tensor = new sidebar.Tensor(argument.initializer);
-                                if (tensor.layout !== '') {
-                                    log("Tensor layout '" + tensor.layout + "' is not implemented.");
-                                }
-                                else if (tensor.format === 0) {
-                                    log('Tensor data not implemented.');
-                                }
-                                else if (tensor.empty) {
-                                    log('Tensor data is empty.');
-                                }
-                                else if (tensor.type && tensor.type.dataType === '?') {
-                                    log('Tensor data type is not defined.');
-                                }
-                                else if (tensor.type && tensor.type.dataType === 'int4') {
-                                    log("Tensor data type 'int4' is not supported.");
-                                }
-                                else if (tensor.type && !tensor.type.shape) {
-                                    log('Tensor shape is not defined.');
-                                }
-                                else {
-                                    tensor.toString();
-                                    // tensor.value;
-                                }
+                            const tensor = new sidebar.Tensor(argument.initializer);
+                            if (tensor.layout !== '<' && tensor.layout !== '>' && tensor.layout !== '|' && tensor.layout !== 'sparse') {
+                                log("Tensor layout '" + tensor.layout + "' is not implemented.");
+                            }
+                            else if (tensor.empty) {
+                                log('Tensor data is empty.');
+                            }
+                            else if (tensor.type && tensor.type.dataType === '?') {
+                                log('Tensor data type is not defined.');
+                            }
+                            else if (tensor.type && !tensor.type.shape) {
+                                log('Tensor shape is not defined.');
                             }
                             else {
-                                argument.initializer.toString();
+                                tensor.toString();
+                                // tensor.value;
                             }
-
                             /*
                             const python = require('../source/python');
                             const tensor = argument.initializer;