Bladeren bron

Update Python execution

Lutz Roeder 5 jaren geleden
bovenliggende
commit
dc6a5c633d
17 gewijzigde bestanden met toevoegingen van 2727 en 2050 verwijderingen
  1. 1 3
      source/acuity.js
  2. 8 5
      source/gzip.js
  3. 0 1
      source/index.html
  4. 20 0
      source/lasagne-metadata.json
  5. 353 0
      source/lasagne.js
  6. 1 2
      source/mxnet.js
  7. 4 118
      source/npz.js
  8. 0 721
      source/pickle.js
  9. 1928 0
      source/python.js
  10. 297 546
      source/pytorch.js
  11. 19 628
      source/sklearn.js
  12. 8 4
      source/tar.js
  13. 1 3
      source/tf.js
  14. 27 4
      source/view.js
  15. 49 4
      source/zip.js
  16. 3 3
      test/models.js
  17. 8 8
      test/models.json

+ 1 - 3
source/acuity.js

@@ -22,9 +22,7 @@ acuity.ModelFactory = class {
             const extension = context.identifier.split('.').pop().toLowerCase();
             switch (extension) {
                 case 'json': {
-                    const buffer = context.stream.peek();
-                    const reader = json.TextReader.create(buffer);
-                    const model = reader.read();
+                    const model = context.tags('json').get('');
                     if (model && model.MetaData && model.Layers) {
                         return new acuity.Model(metadata, model);
                     }

+ 8 - 5
source/gzip.js

@@ -5,14 +5,17 @@ var zip = zip || require('./zip');
 
 gzip.Archive = class {
 
-    constructor(buffer) {
-        this._entries = [];
+    static open(buffer) {
         const stream = buffer instanceof Uint8Array ? new gzip.BinaryReader(buffer) : buffer;
         const signature = [ 0x1f, 0x8b ];
-        if (stream.length < 18 || !stream.peek(2).every((value, index) => value === signature[index])) {
-            throw new gzip.Error('Invalid gzip archive.');
+        if (stream.length > 18 && stream.peek(2).every((value, index) => value === signature[index])) {
+            return new gzip.Archive(stream);
         }
-        this._entries.push(new gzip.Entry(stream));
+        throw new gzip.Error('Invalid gzip archive.');
+    }
+
+    constructor(stream) {
+        this._entries = [ new gzip.Entry(stream) ];
         stream.seek(0);
     }
 

+ 0 - 1
source/index.html

@@ -17,7 +17,6 @@
 <script type="text/javascript" src="dagre.js"></script>
 <script type="text/javascript" src="base.js"></script>
 <script type="text/javascript" src="json.js"></script>
-<script type="text/javascript" src="pickle.js"></script>
 <script type="text/javascript" src="python.js"></script>
 <script type="text/javascript" src="protobuf.js"></script>
 <script type="text/javascript" src="flatbuffers.js"></script>

+ 20 - 0
source/lasagne-metadata.json

@@ -0,0 +1,20 @@
+[
+  {
+    "name": "lasagne.layers.conv.Conv2DLayer",
+    "schema": {
+      "category": "Layer"
+    }
+  },
+  {
+    "name": "lasagne.layers.pool.MaxPool2DLayer",
+    "schema": {
+      "category": "Pool"
+    }
+  },
+  {
+    "name": "lasagne.layers.dense.DenseLayer",
+    "schema": {
+      "category": "Layer"
+    }
+  }
+]

+ 353 - 0
source/lasagne.js

@@ -0,0 +1,353 @@
+/* jshint esversion: 6 */
+
+// Experimental
+
+var lasagne = lasagne || {};
+
+lasagne.ModelFactory = class {
+
+    match(context) {
+        const tags = context.tags('pkl');
+        if (tags.size === 1 && tags.keys().next().value === 'nolearn.lasagne.base.NeuralNet') {
+            return true;
+        }
+        return false;
+    }
+
+    open(context) {
+        return lasagne.Metadata.open(context).then((metadata) => {
+            const model = context.tags('pkl').values().next().value;
+            return new lasagne.Model(metadata, model);
+        });
+    }
+};
+
+lasagne.Model = class {
+
+    constructor(metadata, model) {
+        this._graphs = [ new lasagne.Graph(metadata, model) ];
+    }
+
+    get format() {
+        return 'Lasagne';
+    }
+
+    get graphs() {
+        return this._graphs;
+    }
+};
+
+lasagne.Graph = class {
+
+    constructor(metadata, model) {
+        this._nodes = [];
+        this._inputs = [];
+        this._outputs = [];
+
+        const args = new Map();
+        const arg = (name, type, initializer) => {
+            if (!args.has(name)) {
+                args.set(name, new lasagne.Argument(name, type));
+            }
+            const value = args.get(name);
+            if (!value.type && type) {
+                value.type = type;
+            }
+            if (!value.initializer && initializer) {
+                value.initializer = initializer;
+            }
+            return value;
+        };
+
+        for (const pair of model.layers) {
+            const name = pair[0];
+            const layer = model.layers_[name];
+            if (layer && layer.__module__ === 'lasagne.layers.input' && layer.__name__ === 'InputLayer') {
+                const type = new lasagne.TensorType(layer.input_var.type.dtype, new lasagne.TensorShape(layer.shape));
+                this._inputs.push(new lasagne.Parameter(layer.name, [ arg(layer.name, type) ]));
+                continue;
+            }
+            this._nodes.push(new lasagne.Node(metadata, layer, arg));
+        }
+
+        if (model._output_layer) {
+            const output_layer = model._output_layer;
+            this._outputs.push(new lasagne.Parameter(output_layer.name, [ arg(output_layer.name) ]));
+        }
+    }
+
+    get inputs() {
+        return this._inputs;
+    }
+
+    get outputs() {
+        return this._outputs;
+    }
+
+    get nodes() {
+        return this._nodes;
+    }
+};
+
+lasagne.Parameter = class {
+
+    constructor(name, args) {
+        this._name = name;
+        this._arguments = args;
+    }
+
+    get name() {
+        return this._name;
+    }
+
+    get arguments() {
+        return this._arguments;
+    }
+
+    get visible() {
+        return true;
+    }
+};
+
+lasagne.Argument = class {
+
+    constructor(name, type, initializer) {
+        if (typeof name !== 'string') {
+            throw new lasagne.Error("Invalid argument identifier '" + JSON.stringify(name) + "'.");
+        }
+        this._name= name;
+        this._type = type || null;
+        this._initializer = initializer || null;
+    }
+
+    get name() {
+        return this._name;
+    }
+
+    get type() {
+        if (this._initializer) {
+            return this._initializer.type;
+        }
+        return this._type;
+    }
+
+    set type(value) {
+        this._type = value;
+    }
+
+    get initializer() {
+        return this._initializer;
+    }
+
+    set initializer(value) {
+        this._initializer = value;
+    }
+};
+
+lasagne.Node = class {
+
+    constructor(metadata, layer, arg) {
+        this._name = layer.name || '';
+        this._type = layer.__module__ + '.' + layer.__name__;
+        this._metadata = metadata.type(this._type);
+        this._inputs = [];
+        this._outputs = [];
+        this._attributes = [];
+
+        const params = new Map();
+        for (const key of Object.keys(layer)) {
+            if (key === 'name' || key === 'params' || key === 'input_layer' || key === 'input_shape' || key === '__module__' || key === '__name__') {
+                continue;
+            }
+            const value = layer[key];
+            if (value && value.__module__ === 'theano.tensor.sharedvar' && value.__name__ === 'TensorSharedVariable') {
+                params.set(value.name, key);
+                continue;
+            }
+            this._attributes.push(new lasagne.Attribute(null, key, value));
+        }
+
+        if (layer.input_layer && layer.input_layer.name) {
+            const input_layer = layer.input_layer;
+            const type = layer.input_shape ? new lasagne.TensorType('?', new lasagne.TensorShape(layer.input_shape)) : undefined;
+            this._inputs.push(new lasagne.Parameter('input', [ arg(input_layer.name, type) ]));
+        }
+
+        if (layer.params) {
+            for (const pair of layer.params) {
+                const param = pair[0];
+                const param_key = params.get(param.name);
+                if (param_key) {
+                    const initializer = new lasagne.Tensor(param.container.storage[0]);
+                    this._inputs.push(new lasagne.Parameter(param_key, [ arg(param.name, null, initializer) ]));
+                }
+            }
+        }
+
+        this._outputs.push(new lasagne.Parameter('output', [ arg(this.name) ]));
+    }
+
+    get type() {
+        return this._type;
+    }
+
+    get name() {
+        return this._name;
+    }
+
+    get metadata() {
+        return this._metadata;
+    }
+
+    get inputs() {
+        return this._inputs;
+    }
+
+    get outputs() {
+        return this._outputs;
+    }
+
+    get attributes() {
+        return this._attributes;
+    }
+};
+
+lasagne.Attribute = class {
+
+    constructor(metadata, name, value) {
+        this._name = name;
+        this._value = value;
+    }
+
+    get name() {
+        return this._name;
+    }
+
+    get value() {
+        return this._value;
+    }
+};
+
+lasagne.Metadata = class {
+
+    static open(context) {
+        if (lasagne.Metadata._metadata) {
+            return Promise.resolve(lasagne.Metadata._metadata);
+        }
+        return context.request('lasagne-metadata.json', 'utf-8', null).then((data) => {
+            lasagne.Metadata._metadata = new lasagne.Metadata(data);
+            return lasagne.Metadata._metadata;
+        }).catch(() => {
+            lasagne.Metadata._metadata = new lasagne.Metadata(null);
+            return lasagne.Metadata._metadata;
+        });
+    }
+
+    constructor(data) {
+        this._map = new Map();
+        if (data) {
+            const items = JSON.parse(data);
+            if (items) {
+                for (const item of items) {
+                    if (item.name && item.schema) {
+                        item.schema.name = item.name;
+                        this._map.set(item.name, item.schema);
+                    }
+                }
+            }
+        }
+    }
+
+    type(name) {
+        return this._map.has(name) ? this._map.get(name) : null;
+    }
+
+    attribute(type, name) {
+        const schema = this.type(type);
+        if (schema) {
+            let attributeMap = schema.attributeMap;
+            if (!attributeMap) {
+                attributeMap = {};
+                if (schema.attributes) {
+                    for (const attribute of schema.attributes) {
+                        attributeMap[attribute.name] = attribute;
+                    }
+                }
+                schema.attributeMap = attributeMap;
+            }
+            const attributeSchema = attributeMap[name];
+            if (attributeSchema) {
+                return attributeSchema;
+            }
+        }
+        return null;
+    }
+};
+
+lasagne.TensorType = class {
+
+    constructor(dataType, shape) {
+        this._dataType = dataType;
+        this._shape = shape;
+    }
+
+    get dataType() {
+        return this._dataType;
+    }
+
+    get shape() {
+        return this._shape;
+    }
+
+    toString() {
+        return this._dataType + this._shape.toString();
+    }
+};
+
+lasagne.TensorShape = class {
+
+    constructor(dimensions) {
+        this._dimensions = dimensions;
+    }
+
+    get dimensions() {
+        return this._dimensions;
+    }
+
+    toString() {
+        if (this._dimensions && this._dimensions.length > 0) {
+            return '[' + this._dimensions.map((dimension) => dimension ? dimension.toString() : '?').join(',') + ']';
+        }
+        return '';
+    }
+};
+
+lasagne.Tensor = class {
+
+    constructor(storage) {
+        this._type = new lasagne.TensorType(storage.dtype.name, new lasagne.TensorShape(storage.shape));
+    }
+
+    get type() {
+        return this._type;
+    }
+
+    get state() {
+        return 'Not implemented.';
+    }
+
+    toString() {
+        return '';
+    }
+};
+
+lasagne.Error = class extends Error {
+    constructor(message) {
+        super(message);
+        this.name = 'Lasagne Error';
+    }
+};
+
+if (typeof module !== 'undefined' && typeof module.exports === 'object') {
+    module.exports.ModelFactory = lasagne.ModelFactory;
+}

+ 1 - 2
source/mxnet.js

@@ -42,8 +42,7 @@ mxnet.ModelFactory = class {
             switch (extension) {
                 case 'json':
                     try {
-                        const reader = json.TextReader.create(context.stream.peek());
-                        symbol = reader.read();
+                        symbol = context.tags('json').get('');
                         if (symbol && symbol.nodes && symbol.nodes.some((node) => node && node.op == 'tvm_op')) {
                             format  = 'TVM';
                         }

+ 4 - 118
source/npz.js

@@ -3,7 +3,7 @@
 // Experimental
 
 var npz = npz || {};
-var pickle = pickle || require('./pickle');
+var python = python || require('./python');
 
 npz.ModelFactory = class {
 
@@ -16,126 +16,12 @@ npz.ModelFactory = class {
         return context.require('./numpy').then((numpy) => {
             const modules = [];
             const modulesMap = new Map();
-            const functionTable = new Map();
-            const constructorTable = new Map();
-            functionTable.set('_codecs.encode', function(obj /*, econding */) {
-                return obj;
-            });
-            constructorTable.set('numpy.core.multiarray._reconstruct', function(subtype, shape, dtype) {
-                this.subtype = subtype;
-                this.shape = shape;
-                this.dtype = dtype;
-                this.__setstate__ = function(state) {
-                    this.version = state[0];
-                    this.shape = state[1];
-                    this.typecode = state[2];
-                    this.is_f_order = state[3];
-                    this.rawdata = state[4];
-                };
-                this.__read__ = function(unpickler) {
-                    const array = {};
-                    array.__type__ = this.subtype;
-                    array.dtype = this.typecode;
-                    array.shape = this.shape;
-                    let size = array.dtype.itemsize;
-                    for (let i = 0; i < array.shape.length; i++) {
-                        size = size * array.shape[i];
-                    }
-                    if (typeof this.rawdata == 'string') {
-                        array.data = unpickler.unescape(this.rawdata, size);
-                        if (array.data.length != size) {
-                            throw new npz.Error('Invalid string array data size.');
-                        }
-                    }
-                    else {
-                        array.data = this.rawdata;
-                        if (array.data.length != size) {
-                            // TODO
-                            // throw new npz.Error('Invalid array data size.');
-                        }
-                    }
-                    return array;
-                };
-            });
-            constructorTable.set('numpy.dtype', function(obj, align, copy) {
-                switch (obj) {
-                    case 'i1': this.name = 'int8'; this.itemsize = 1; break;
-                    case 'i2': this.name = 'int16'; this.itemsize = 2; break;
-                    case 'i4': this.name = 'int32'; this.itemsize = 4; break;
-                    case 'i8': this.name = 'int64'; this.itemsize = 8; break;
-                    case 'u1': this.name = 'uint8'; this.itemsize = 1; break;
-                    case 'u2': this.name = 'uint16'; this.itemsize = 2; break;
-                    case 'u4': this.name = 'uint32'; this.itemsize = 4; break;
-                    case 'u8': this.name = 'uint64'; this.itemsize = 8; break;
-                    case 'f4': this.name = 'float32'; this.itemsize = 4; break;
-                    case 'f8': this.name = 'float64'; this.itemsize = 8; break;
-                    default:
-                        if (obj.startsWith('V')) {
-                            this.itemsize = Number(obj.substring(1));
-                            this.name = 'void' + (this.itemsize * 8).toString();
-                        }
-                        else if (obj.startsWith('O')) {
-                            this.itemsize = Number(obj.substring(1));
-                            this.name = 'object';
-                        }
-                        else if (obj.startsWith('S')) {
-                            this.itemsize = Number(obj.substring(1));
-                            this.name = 'string';
-                        }
-                        else if (obj.startsWith('U')) {
-                            this.itemsize = Number(obj.substring(1));
-                            this.name = 'string';
-                        }
-                        else if (obj.startsWith('M')) {
-                            this.itemsize = Number(obj.substring(1));
-                            this.name = 'datetime';
-                        }
-                        else {
-                            throw new npz.Error("Unknown dtype '" + obj.toString() + "'.");
-                        }
-                        break;
-                }
-                this.align = align;
-                this.copy = copy;
-                this.__setstate__ = function(state) {
-                    switch (state.length) {
-                        case 8:
-                            this.version = state[0];
-                            this.byteorder = state[1];
-                            this.subarray = state[2];
-                            this.names = state[3];
-                            this.fields = state[4];
-                            this.elsize = state[5];
-                            this.alignment = state[6];
-                            this.int_dtypeflags = state[7];
-                            break;
-                        default:
-                            throw new npz.Error("Unknown numpy.dtype setstate length '" + state.length.toString() + "'.");
-                    }
-                };
-            });
-            const function_call = (name, args) => {
-                if (functionTable.has(name)) {
-                    const func = functionTable.get(name);
-                    return func.apply(null, args);
-                }
-                const obj = { __type__: name };
-                if (constructorTable.has(name)) {
-                    const constructor = constructorTable.get(name);
-                    constructor.apply(obj, args);
-                }
-                else {
-                    throw new npz.Error("Unknown function '" + name + "'.");
-                }
-                return obj;
-            };
-
             const dataTypeMap = new Map([
                 [ 'i1', 'int8'], [ 'i2', 'int16' ], [ 'i4', 'int32'], [ 'i8', 'int64' ],
                 [ 'u1', 'uint8'], [ 'u2', 'uint16' ], [ 'u4', 'uint32'], [ 'u8', 'uint64' ],
                 [ 'f2', 'float16'], [ 'f4', 'float32' ], [ 'f8', 'float64']
             ]);
-
+            const execution = new python.Execution(null);
             for (const entry of context.entries('zip')) {
                 if (!entry.name.endsWith('.npy')) {
                     throw new npz.Error("Invalid file name '" + entry.name + "'.");
@@ -156,8 +42,8 @@ npz.ModelFactory = class {
                     if (array.dataType !== 'O') {
                         throw new npz.Error("Invalid data type '" + array.dataType + "'.");
                     }
-                    const unpickler = new pickle.Unpickler(array.data);
-                    const root = unpickler.load(function_call);
+                    const unpickler = new python.Unpickler(array.data);
+                    const root = unpickler.load((name, args) => execution.invoke(name, args));
                     array = { dataType: root.dtype.name, shape: null, data: null, byteOrder: '|' };
                 }
 

+ 0 - 721
source/pickle.js

@@ -1,721 +0,0 @@
-/* jshint esversion: 6 */
-
-var pickle = pickle || {};
-
-pickle.Unpickler = class {
-
-    constructor(buffer) {
-        this._reader = buffer instanceof Uint8Array ? new pickle.BinaryReader(buffer) : new pickle.StreamReader(buffer);
-    }
-
-    load(function_call, persistent_load) {
-        const reader = this._reader;
-        const marker = [];
-        let stack = [];
-        const memo = new Map();
-        while (reader.position < reader.length) {
-            const opcode = reader.byte();
-            switch (opcode) {
-                case pickle.OpCode.PROTO: {
-                    const version = reader.byte();
-                    if (version > 5) {
-                        throw new pickle.Error("Unsupported protocol version '" + version + "'.");
-                    }
-                    break;
-                }
-                case pickle.OpCode.GLOBAL:
-                    stack.push([ reader.line(), reader.line() ].join('.'));
-                    break;
-                case pickle.OpCode.STACK_GLOBAL:
-                    stack.push([ stack.pop(), stack.pop() ].reverse().join('.'));
-                    break;
-                case pickle.OpCode.PUT: {
-                    const index = parseInt(reader.line(), 10);
-                    memo.set(index, stack[stack.length - 1]);
-                    break;
-                }
-                case pickle.OpCode.OBJ: {
-                    const items = stack;
-                    stack = marker.pop();
-                    stack.push(function_call(items.pop(), items));
-                    break;
-                }
-                case pickle.OpCode.GET: {
-                    const index = parseInt(reader.line(), 10);
-                    stack.push(memo.get(index));
-                    break;
-                }
-                case pickle.OpCode.POP:
-                    stack.pop();
-                    break;
-                case pickle.OpCode.POP_MARK:
-                    stack = marker.pop();
-                    break;
-                case pickle.OpCode.DUP:
-                    stack.push(stack[stack.length-1]);
-                    break;
-                case pickle.OpCode.PERSID:
-                    stack.push(persistent_load(reader.line()));
-                    break;
-                case pickle.OpCode.BINPERSID:
-                    stack.push(persistent_load(stack.pop()));
-                    break;
-                case pickle.OpCode.REDUCE: {
-                    const items = stack.pop();
-                    const type = stack.pop();
-                    stack.push(function_call(type, items));
-                    break;
-                }
-                case pickle.OpCode.NEWOBJ: {
-                    const items = stack.pop();
-                    const type = stack.pop();
-                    stack.push(function_call(type, items));
-                    break;
-                }
-                case pickle.OpCode.BINGET:
-                    stack.push(memo.get(reader.byte()));
-                    break;
-                case pickle.OpCode.LONG_BINGET:
-                    stack.push(memo.get(reader.uint32()));
-                    break;
-                case pickle.OpCode.BINPUT:
-                    memo.set(reader.byte(), stack[stack.length - 1]);
-                    break;
-                case pickle.OpCode.LONG_BINPUT:
-                    memo.set(reader.uint32(), stack[stack.length - 1]);
-                    break;
-                case pickle.OpCode.BININT:
-                    stack.push(reader.int32());
-                    break;
-                case pickle.OpCode.BININT1:
-                    stack.push(reader.byte());
-                    break;
-                case pickle.OpCode.LONG:
-                    stack.push(parseInt(reader.line(), 10));
-                    break;
-                case pickle.OpCode.BININT2:
-                    stack.push(reader.uint16());
-                    break;
-                case pickle.OpCode.BINBYTES:
-                    stack.push(reader.read(reader.int32()));
-                    break;
-                case pickle.OpCode.BINBYTES8:
-                    stack.push(reader.read(reader.int64()));
-                    break;
-                case pickle.OpCode.SHORT_BINBYTES:
-                    stack.push(reader.read(reader.byte()));
-                    break;
-                case pickle.OpCode.FLOAT:
-                    stack.push(parseFloat(reader.line()));
-                    break;
-                case pickle.OpCode.BINFLOAT:
-                    stack.push(reader.float64());
-                    break;
-                case pickle.OpCode.INT: {
-                    const value = reader.line();
-                    if (value == '01') {
-                        stack.push(true);
-                    }
-                    else if (value == '00') {
-                        stack.push(false);
-                    }
-                    else {
-                        stack.push(parseInt(value, 10));
-                    }
-                    break;
-                }
-                case pickle.OpCode.EMPTY_LIST:
-                    stack.push([]);
-                    break;
-                case pickle.OpCode.EMPTY_TUPLE:
-                    stack.push([]);
-                    break;
-                case pickle.OpCode.EMPTY_SET:
-                    stack.push([]);
-                    break;
-                case pickle.OpCode.ADDITEMS: {
-                    const items = stack;
-                    stack = marker.pop();
-                    const obj = stack[stack.length - 1];
-                    for (let i = 0; i < items.length; i++) {
-                        obj.push(items[i]);
-                    }
-                    break;
-                }
-                case pickle.OpCode.DICT: {
-                    const items = stack;
-                    stack = marker.pop();
-                    const dict = {};
-                    for (let i = 0; i < items.length; i += 2) {
-                        dict[items[i]] = items[i + 1];
-                    }
-                    stack.push(dict);
-                    break;
-                }
-                case pickle.OpCode.LIST: {
-                    const items = stack;
-                    stack = marker.pop();
-                    stack.push(items);
-                    break;
-                }
-                case pickle.OpCode.TUPLE: {
-                    const items = stack;
-                    stack = marker.pop();
-                    stack.push(items);
-                    break;
-                }
-                case pickle.OpCode.SETITEM: {
-                    const value = stack.pop();
-                    const key = stack.pop();
-                    const obj = stack[stack.length - 1];
-                    if (obj.__setitem__) {
-                        obj.__setitem__(key, value);
-                    }
-                    else {
-                        obj[key] = value;
-                    }
-                    break;
-                }
-                case pickle.OpCode.SETITEMS: {
-                    const items = stack;
-                    stack = marker.pop();
-                    const obj = stack[stack.length - 1];
-                    for (let i = 0; i < items.length; i += 2) {
-                        if (obj.__setitem__) {
-                            obj.__setitem__(items[i], items[i + 1]);
-                        }
-                        else {
-                            obj[items[i]] = items[i + 1];
-                        }
-                    }
-                    break;
-                }
-                case pickle.OpCode.EMPTY_DICT:
-                    stack.push({});
-                    break;
-                case pickle.OpCode.APPEND: {
-                    const append = stack.pop();
-                    stack[stack.length-1].push(append);
-                    break;
-                }
-                case pickle.OpCode.APPENDS: {
-                    const appends = stack;
-                    stack = marker.pop();
-                    const list = stack[stack.length - 1];
-                    list.push.apply(list, appends);
-                    break;
-                }
-                case pickle.OpCode.STRING: {
-                    const str = reader.line();
-                    stack.push(str.substr(1, str.length - 2));
-                    break;
-                }
-                case pickle.OpCode.BINSTRING:
-                    stack.push(reader.string(reader.uint32()));
-                    break;
-                case pickle.OpCode.SHORT_BINSTRING:
-                    stack.push(reader.string(reader.byte()));
-                    break;
-                case pickle.OpCode.UNICODE:
-                    stack.push(reader.line());
-                    break;
-                case pickle.OpCode.BINUNICODE:
-                    stack.push(reader.string(reader.uint32(), 'utf-8'));
-                    break;
-                case pickle.OpCode.SHORT_BINUNICODE:
-                    stack.push(reader.string(reader.byte(), 'utf-8'));
-                    break;
-                case pickle.OpCode.BUILD: {
-                    const state = stack.pop();
-                    let obj = stack.pop();
-                    if (obj.__setstate__) {
-                        if (obj.__setstate__.__call__) {
-                            obj.__setstate__.__call__([ obj, state ]);
-                        }
-                        else {
-                            obj.__setstate__(state);
-                        }
-                    }
-                    else if (ArrayBuffer.isView(state) || Object(state) !== state) {
-                        throw new pickle.Error('Invalid state dict' + (obj && obj.__module__ && obj.__name__ ? " for '" + obj.__module__ + '.' + obj.__name__ + "'": '') + '.');
-                    }
-                    else if (obj instanceof Map) {
-                        for (const key in state) {
-                            obj.set(key, state[key]);
-                        }
-                    }
-                    else {
-                        Object.assign(obj, state);
-                    }
-                    if (obj.__read__) {
-                        obj = obj.__read__(this);
-                    }
-                    stack.push(obj);
-                    break;
-                }
-                case pickle.OpCode.MARK:
-                    marker.push(stack);
-                    stack = [];
-                    break;
-                case pickle.OpCode.NEWTRUE:
-                    stack.push(true);
-                    break;
-                case pickle.OpCode.NEWFALSE:
-                    stack.push(false);
-                    break;
-                case pickle.OpCode.LONG1: {
-                    const data = reader.read(reader.byte());
-                    let number = 0;
-                    switch (data.length) {
-                        case 0: number = 0; break;
-                        case 1: number = data[0]; break;
-                        case 2: number = data[1] << 8 | data[0]; break;
-                        case 3: number = data[2] << 16 | data[1] << 8 | data[0]; break;
-                        case 4: number = data[3] << 24 | data[2] << 16 | data[1] << 8 | data[0]; break;
-                        case 5: number = data[4] * 0x100000000 + ((data[3] << 24 | data[2] << 16 | data[1] << 8 | data[0]) >>> 0); break;
-                        default: number = Array.prototype.slice.call(data, 0); break;
-                    }
-                    stack.push(number);
-                    break;
-                }
-                case pickle.OpCode.LONG4:
-                    // TODO decode LONG4
-                    stack.push(reader.read(reader.uint32()));
-                    break;
-                case pickle.OpCode.TUPLE1:
-                    stack.push([ stack.pop() ]);
-                    break;
-                case pickle.OpCode.TUPLE2: {
-                    const b = stack.pop();
-                    const a = stack.pop();
-                    stack.push([ a, b ]);
-                    break;
-                }
-                case pickle.OpCode.TUPLE3: {
-                    const c = stack.pop();
-                    const b = stack.pop();
-                    const a = stack.pop();
-                    stack.push([ a, b, c ]);
-                    break;
-                }
-                case pickle.OpCode.MEMOIZE:
-                    memo.set(memo.size, stack[stack.length - 1]);
-                    break;
-                case pickle.OpCode.FRAME:
-                    reader.read(8);
-                    break;
-                case pickle.OpCode.BYTEARRAY8: {
-                    stack.push(reader.read(reader.int64()));
-                    break;
-                }
-                case pickle.OpCode.NONE:
-                    stack.push(null);
-                    break;
-                case pickle.OpCode.STOP:
-                    return stack.pop();
-                default:
-                    throw new pickle.Error("Unknown opcode '" + opcode + "'.");
-            }
-        }
-        throw new pickle.Error('Unexpected end of file.');
-    }
-
-    read(size) {
-        return this._reader.read(size);
-    }
-
-    stream(size) {
-        return this._reader.stream(size);
-    }
-
-    unescape(token, size) {
-        const length = token.length;
-        const a = new Uint8Array(length);
-        if (size && size == length) {
-            for (let p = 0; p < size; p++) {
-                a[p] = token.charCodeAt(p);
-            }
-            return a;
-        }
-        let i = 0;
-        let o = 0;
-        while (i < length) {
-            let c = token.charCodeAt(i++);
-            if (c !== 0x5C || i >= length) {
-                a[o++] = c;
-            }
-            else {
-                c = token.charCodeAt(i++);
-                switch (c) {
-                    case 0x27: a[o++] = 0x27; break; // '
-                    case 0x5C: a[o++] = 0x5C; break; // \\
-                    case 0x22: a[o++] = 0x22; break; // "
-                    case 0x72: a[o++] = 0x0D; break; // \r
-                    case 0x6E: a[o++] = 0x0A; break; // \n
-                    case 0x74: a[o++] = 0x09; break; // \t
-                    case 0x62: a[o++] = 0x08; break; // \b
-                    case 0x58: // x
-                    case 0x78: { // X
-                        const xsi = i - 1;
-                        const xso = o;
-                        for (let xi = 0; xi < 2; xi++) {
-                            if (i >= length) {
-                                i = xsi;
-                                o = xso;
-                                a[o] = 0x5c;
-                                break;
-                            }
-                            let xd = token.charCodeAt(i++);
-                            xd = xd >= 65 && xd <= 70 ? xd - 55 : xd >= 97 && xd <= 102 ? xd - 87 : xd >= 48 && xd <= 57 ? xd - 48 : -1;
-                            if (xd === -1) {
-                                i = xsi;
-                                o = xso;
-                                a[o] = 0x5c;
-                                break;
-                            }
-                            a[o] = a[o] << 4 | xd;
-                        }
-                        o++;
-                        break;
-                    }
-                    default:
-                        if (c < 48 || c > 57) { // 0-9
-                            a[o++] = 0x5c;
-                            a[o++] = c;
-                        }
-                        else {
-                            i--;
-                            const osi = i;
-                            const oso = o;
-                            for (let oi = 0; oi < 3; oi++) {
-                                if (i >= length) {
-                                    i = osi;
-                                    o = oso;
-                                    a[o] = 0x5c;
-                                    break;
-                                }
-                                const od = token.charCodeAt(i++);
-                                if (od < 48 || od > 57) {
-                                    i = osi;
-                                    o = oso;
-                                    a[o] = 0x5c;
-                                    break;
-                                }
-                                a[o] = a[o] << 3 | od - 48;
-                            }
-                            o++;
-                        }
-                        break;
-                }
-            }
-        }
-        return a.slice(0, o);
-    }
-};
-
-// https://svn.python.org/projects/python/trunk/Lib/pickletools.py
-// https://github.com/python/cpython/blob/master/Lib/pickle.py
-pickle.OpCode = {
-    MARK: 40,              // '('
-    EMPTY_TUPLE: 41,       // ')'
-    STOP: 46,              // '.'
-    POP: 48,               // '0'
-    POP_MARK: 49,          // '1'
-    DUP: 50,               // '2'
-    BINBYTES: 66,          // 'B' (Protocol 3)
-    SHORT_BINBYTES: 67,    // 'C' (Protocol 3)
-    FLOAT: 70,             // 'F'
-    BINFLOAT: 71,          // 'G'
-    INT: 73,               // 'I'
-    BININT: 74,            // 'J'
-    BININT1: 75,           // 'K'
-    LONG: 76,              // 'L'
-    BININT2: 77,           // 'M'
-    NONE: 78,              // 'N'
-    PERSID: 80,            // 'P'
-    BINPERSID: 81,         // 'Q'
-    REDUCE: 82,            // 'R'
-    STRING: 83,             // 'S'
-    BINSTRING: 84,         // 'T'
-    SHORT_BINSTRING: 85,   // 'U'
-    UNICODE: 86,           // 'V'
-    BINUNICODE: 88,        // 'X'
-    EMPTY_LIST: 93,        // ']'
-    APPEND: 97,            // 'a'
-    BUILD: 98,             // 'b'
-    GLOBAL: 99,            // 'c'
-    DICT: 100,             // 'd'
-    APPENDS: 101,          // 'e'
-    GET: 103,              // 'g'
-    BINGET: 104,           // 'h'
-    LONG_BINGET: 106,      // 'j'
-    LIST: 108,             // 'l'
-    OBJ: 111,              // 'o'
-    PUT: 112,              // 'p'
-    BINPUT: 113,           // 'q'
-    LONG_BINPUT: 114,      // 'r'
-    SETITEM: 115,          // 's'
-    TUPLE: 116,            // 't'
-    SETITEMS: 117,         // 'u'
-    EMPTY_DICT: 125,       // '}'
-    PROTO: 128,
-    NEWOBJ: 129,
-    TUPLE1: 133,           // '\x85'
-    TUPLE2: 134,           // '\x86'
-    TUPLE3: 135,           // '\x87'
-    NEWTRUE: 136,          // '\x88'
-    NEWFALSE: 137,         // '\x89'
-    LONG1: 138,            // '\x8a'
-    LONG4: 139,            // '\x8b'
-    SHORT_BINUNICODE: 140, // '\x8c' (Protocol 4)
-    BINUNICODE8: 141,      // '\x8d' (Protocol 4)
-    BINBYTES8: 142,        // '\x8e' (Protocol 4)
-    EMPTY_SET: 143,        // '\x8f' (Protocol 4)
-    ADDITEMS: 144,         // '\x90' (Protocol 4)
-    FROZENSET: 145,        // '\x91' (Protocol 4)
-    NEWOBJ_EX: 146,        // '\x92' (Protocol 4)
-    STACK_GLOBAL: 147,     // '\x93' (Protocol 4)
-    MEMOIZE: 148,          // '\x94' (Protocol 4)
-    FRAME: 149,            // '\x95' (Protocol 4)
-    BYTEARRAY8: 150,       // '\x96' (Protocol 5)
-    NEXT_BUFFER: 151,      // '\x97' (Protocol 5)
-    READONLY_BUFFER: 152   // '\x98' (Protocol 5)
-};
-
-pickle.BinaryReader = class {
-
-    constructor(buffer) {
-        this._buffer = buffer;
-        this._length = buffer.length;
-        this._position = 0;
-        this._dataView = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
-        this._utf8Decoder = new TextDecoder('utf-8');
-        this._asciiDecoder = new TextDecoder('ascii');
-    }
-
-    get position() {
-        return this._position;
-    }
-
-    get length() {
-        return this._length;
-    }
-
-    skip(offset) {
-        this._position += offset;
-        if (this._position > this._buffer.length) {
-            throw new pickle.Error('Expected ' + (this._position - this._buffer.length) + ' more bytes. The file might be corrupted. Unexpected end of file.');
-        }
-    }
-
-    stream(length) {
-        const buffer = this.read(length);
-        return new pickle.BinaryReader(buffer);
-    }
-
-    peek(length) {
-        const position = this._position;
-        length = length !== undefined ? length : this._length - this._position;
-        this.skip(length);
-        const end = this._position;
-        this.skip(-length);
-        if (position === 0 && length === this._length) {
-            return this._buffer;
-        }
-        return this._buffer.subarray(position, end);
-    }
-
-    read(length) {
-        const position = this._position;
-        length = length !== undefined ? length : this._length - this._position;
-        this.skip(length);
-        if (position === 0 && length === this._length) {
-            return this._buffer;
-        }
-        return this._buffer.subarray(position, this._position);
-    }
-
-    byte() {
-        const position = this._position;
-        this.skip(1);
-        return this._dataView.getUint8(position);
-    }
-
-    uint16() {
-        const position = this._position;
-        this.skip(2);
-        return this._dataView.getUint16(position, true);
-    }
-
-    int32() {
-        const position = this._position;
-        this.skip(4);
-        return this._dataView.getInt32(position, true);
-    }
-
-    uint32() {
-        const position = this._position;
-        this.skip(4);
-        return this._dataView.getUint32(position, true);
-    }
-
-    int64() {
-        const low = this.uint32();
-        const high = this.uint32();
-        if (high !== 0) {
-            throw new pickle.Error('Unsupported 64-bit integer value.');
-        }
-        return low;
-    }
-
-    float32() {
-        const position = this._position;
-        this.skip(4);
-        return this._dataView.getFloat32(position, true);
-    }
-
-    float64() {
-        const position = this._position;
-        this.skip(8);
-        return this._dataView.getFloat64(position, true);
-    }
-
-    string(size, encoding) {
-        const data = this.read(size);
-        return (encoding == 'utf-8') ?
-            this._utf8Decoder.decode(data) :
-            this._asciiDecoder.decode(data);
-    }
-
-    line() {
-        const index = this._buffer.indexOf(0x0A, this._position);
-        if (index == -1) {
-            throw new pickle.Error("Could not find end of line.");
-        }
-        const size = index - this._position;
-        const text = this.string(size, 'ascii');
-        this.skip(1);
-        return text;
-    }
-};
-
-pickle.StreamReader = class {
-
-    constructor(stream) {
-        this._stream = stream;
-        this._length = stream.length;
-        this._position = 0;
-        this._utf8Decoder = new TextDecoder('utf-8');
-        this._asciiDecoder = new TextDecoder('ascii');
-    }
-
-    get position() {
-        return this._position;
-    }
-
-    get length() {
-        return this._length;
-    }
-
-    skip(offset) {
-        this._position += offset;
-        if (this._position > this._length) {
-            throw new pickle.Error('Expected ' + (this._position - this._length) + ' more bytes. The file might be corrupted. Unexpected end of file.');
-        }
-    }
-
-    stream(length) {
-        this._stream.seek(this._position);
-        this.skip(length);
-        return this._stream.stream(length);
-    }
-
-    read(length) {
-        this._stream.seek(this._position);
-        this.skip(length);
-        return this._stream.read(length);
-    }
-
-    byte() {
-        const position = this._fill(1);
-        return this._dataView.getUint8(position);
-    }
-
-    uint16() {
-        const position = this._fill(2);
-        return this._dataView.getUint16(position, true);
-    }
-
-    int32() {
-        const position = this._fill(4);
-        return this._dataView.getInt32(position, true);
-    }
-
-    uint32() {
-        const position = this._fill(4);
-        return this._dataView.getUint32(position, true);
-    }
-
-    int64() {
-        const low = this.uint32();
-        const high = this.uint32();
-        if (high !== 0) {
-            throw new pickle.Error('Unsupported 64-bit integer value.');
-        }
-        return low;
-    }
-
-    float32() {
-        const position = this._fill(4);
-        return this._dataView.getFloat32(position, true);
-    }
-
-    float64() {
-        const position = this._fill(8);
-        return this._dataView.getFloat64(position, true);
-    }
-
-    string(size, encoding) {
-        const data = this.read(size);
-        return (encoding == 'utf-8') ?
-            this._utf8Decoder.decode(data) :
-            this._asciiDecoder.decode(data);
-    }
-
-    line() {
-        const index = this._buffer.indexOf(0x0A, this._position);
-        if (index == -1) {
-            throw new pickle.Error("Could not find end of line.");
-        }
-        const size = index - this._position;
-        const text = this.string(size, 'ascii');
-        this.skip(1);
-        return text;
-    }
-
-    _fill(length) {
-        if (this._position + length > this._length) {
-            throw new Error('Expected ' + (this._position + length - this._length) + ' more bytes. The file might be corrupted. Unexpected end of file.');
-        }
-        if (!this._buffer || this._position < this._offset || this._position + length > this._offset + this._buffer.length) {
-            this._offset = this._position;
-            this._stream.seek(this._offset);
-            this._buffer = this._stream.read(Math.min(0x10000000, this._length - this._offset));
-            this._dataView = new DataView(this._buffer.buffer, this._buffer.byteOffset, this._buffer.byteLength);
-        }
-        const position = this._position;
-        this._position += length;
-        return position - this._offset;
-    }
-};
-
-pickle.Error = class extends Error {
-    constructor(message) {
-        super(message);
-        this.name = 'Unpickle Error';
-    }
-};
-
-if (typeof module !== 'undefined' && typeof module.exports === 'object') {
-    module.exports.Unpickler = pickle.Unpickler;
-}

File diff suppressed because it is too large
+ 1928 - 0
source/python.js


File diff suppressed because it is too large
+ 297 - 546
source/pytorch.js


+ 19 - 628
source/sklearn.js

@@ -3,12 +3,13 @@
 // Experimental
 
 var sklearn = sklearn || {};
-var pickle = pickle || require('./pickle');
+var python = python || require('./python');
 var zip = zip || require('./zip');
 
 sklearn.ModelFactory = class {
 
     match(context) {
+
         const stream = context.stream;
         const signature = [ 0x80, undefined, 0x8a, 0x0a, 0x6c, 0xfc, 0x9c, 0x46, 0xf9, 0x20, 0x6a, 0xa8, 0x50, 0x19 ];
         if (signature.length <= stream.length && stream.peek(signature.length).every((value, index) => signature[index] === undefined || signature[index] === value)) {
@@ -41,7 +42,7 @@ sklearn.ModelFactory = class {
             let container;
             try {
                 const buffer = context.stream.peek();
-                container = new sklearn.Container(buffer, pickle, (error, fatal) => {
+                container = new sklearn.Container(buffer, (error, fatal) => {
                     const message = error && error.message ? error.message : error.toString();
                     context.exception(new sklearn.Error(message.replace(/\.$/, '') + " in '" + identifier + "'."), fatal);
                 });
@@ -664,614 +665,15 @@ sklearn.Metadata = class {
 
 sklearn.Container = class {
 
-    constructor(buffer, pickle, exception) {
+    constructor(buffer, exception) {
         if (buffer.length > 0 && buffer[0] == 0x78) {
             buffer = buffer.subarray(2, buffer.length - 2);
             buffer = new zip.Inflater().inflateRaw(buffer);
         }
-
-        const unpickler = new pickle.Unpickler(buffer);
-
-        const constructorTable = {};
-        const functionTable = {};
-
-        constructorTable['argparse.Namespace'] = function (args) {
-            this.args = args;
-        };
-        constructorTable['gensim.models.doc2vec.Doctag'] = function() {};
-        constructorTable['gensim.models.doc2vec.Doc2Vec'] = function() {};
-        constructorTable['gensim.models.doc2vec.Doc2VecTrainables'] = function() {};
-        constructorTable['gensim.models.doc2vec.Doc2VecVocab'] = function() {};
-        constructorTable['gensim.models.fasttext.FastText'] = function() {};
-        constructorTable['gensim.models.fasttext.FastTextTrainables'] = function() {};
-        constructorTable['gensim.models.fasttext.FastTextVocab'] = function() {};
-        constructorTable['gensim.models.fasttext.FastTextKeyedVectors'] = function() {};
-        constructorTable['gensim.models.keyedvectors.Doc2VecKeyedVectors'] = function() {};
-        constructorTable['gensim.models.keyedvectors.FastTextKeyedVectors'] = function() {};
-        constructorTable['gensim.models.keyedvectors.Vocab'] = function() {};
-        constructorTable['gensim.models.keyedvectors.Word2VecKeyedVectors'] = function() {};
-        constructorTable['gensim.models.phrases.Phrases'] = function() {};
-        constructorTable['gensim.models.tfidfmodel.TfidfModel'] = function() {};
-        constructorTable['gensim.models.word2vec.Vocab'] = function() {};
-        constructorTable['gensim.models.word2vec.Word2Vec'] = function() {};
-        constructorTable['gensim.models.word2vec.Word2VecTrainables'] = function() {};
-        constructorTable['gensim.models.word2vec.Word2VecVocab'] = function() {};
-        constructorTable['joblib.numpy_pickle.NumpyArrayWrapper'] = function(/* subtype, shape, dtype */) {
-            this.__setstate__ = function(state) {
-                this.subclass = state.subclass;
-                this.dtype = state.dtype;
-                this.shape = state.shape;
-                this.order = state.order;
-                this.allow_mmap = state.allow_mmap;
-            };
-            this.__read__ = function(unpickler) {
-                if (this.dtype.name == 'object') {
-                    return unpickler.load(function_call, null);
-                }
-                else {
-                    const size = this.dtype.itemsize * this.shape.reduce((a, b) => a * b);
-                    this.data = unpickler.read(size);
-                }
-                const obj = {
-                    dtype: this.dtype,
-                    shape: this.shape,
-                    data: this.data,
-                };
-                sklearn.Utility.applyType(obj, this.subclass);
-                return obj;
-            };
-        };
-        constructorTable['lightgbm.sklearn.LGBMRegressor'] = function() {};
-        constructorTable['lightgbm.sklearn.LGBMClassifier'] = function() {};
-        constructorTable['lightgbm.basic.Booster'] = function() {};
-        constructorTable['nolearn.lasagne.base.BatchIterator'] = function() {};
-        constructorTable['nolearn.lasagne.base.Layers'] = function() {};
-        constructorTable['nolearn.lasagne.base.NeuralNet'] = function() {};
-        constructorTable['nolearn.lasagne.base.TrainSplit'] = function() {};
-        constructorTable['nolearn.lasagne.handlers.PrintLayerInfo'] = function() {};
-        constructorTable['nolearn.lasagne.handlers.PrintLog'] = function() {};
-        constructorTable['numpy.dtype'] = function(obj, align, copy) {
-            switch (obj) {
-                case 'i1': this.name = 'int8'; this.itemsize = 1; break;
-                case 'i2': this.name = 'int16'; this.itemsize = 2; break;
-                case 'i4': this.name = 'int32'; this.itemsize = 4; break;
-                case 'i8': this.name = 'int64'; this.itemsize = 8; break;
-                case 'u1': this.name = 'uint8'; this.itemsize = 1; break;
-                case 'u2': this.name = 'uint16'; this.itemsize = 2; break;
-                case 'u4': this.name = 'uint32'; this.itemsize = 4; break;
-                case 'u8': this.name = 'uint64'; this.itemsize = 8; break;
-                case 'f2': this.name = 'float16'; this.itemsize = 2; break;
-                case 'f4': this.name = 'float32'; this.itemsize = 4; break;
-                case 'f8': this.name = 'float64'; this.itemsize = 8; break;
-                case 'b1': this.name = 'int8'; this.itemsize = 1; break;
-                default:
-                    if (obj.startsWith('V')) {
-                        this.itemsize = Number(obj.substring(1));
-                        this.name = 'void' + (this.itemsize * 8).toString();
-                    }
-                    else if (obj.startsWith('O')) {
-                        this.itemsize = Number(obj.substring(1));
-                        this.name = 'object';
-                    }
-                    else if (obj.startsWith('S')) {
-                        this.itemsize = Number(obj.substring(1));
-                        this.name = 'string';
-                    }
-                    else if (obj.startsWith('U')) {
-                        this.itemsize = Number(obj.substring(1));
-                        this.name = 'string';
-                    }
-                    else if (obj.startsWith('M')) {
-                        this.itemsize = Number(obj.substring(1));
-                        this.name = 'datetime';
-                    }
-                    else {
-                        throw new sklearn.Error("Unknown dtype '" + obj.toString() + "'.");
-                    }
-                    break;
-            }
-            this.align = align;
-            this.copy = copy;
-            this.__setstate__ = function(state) {
-                switch (state.length) {
-                    case 8:
-                        this.version = state[0];
-                        this.byteorder = state[1];
-                        this.subarray = state[2];
-                        this.names = state[3];
-                        this.fields = state[4];
-                        this.elsize = state[5];
-                        this.alignment = state[6];
-                        this.int_dtypeflags = state[7];
-                        break;
-                    case 9:
-                        this.version = state[0];
-                        this.byteorder = state[1];
-                        this.subarray = state[2];
-                        this.names = state[3];
-                        this.fields = state[4];
-                        this.elsize = state[5];
-                        this.alignment = state[6];
-                        this.int_dtypeflags = state[7];
-                        this.metadata = state[8];
-                        break;
-                    default:
-                        throw new sklearn.Error("Unknown numpy.dtype setstate length '" + state.length.toString() + "'.");
-                }
-            };
-        };
-        constructorTable['numpy.core.multiarray._reconstruct'] = function(subtype, shape, dtype) {
-            this.subtype = subtype;
-            this.shape = shape;
-            this.dtype = dtype;
-            this.__setstate__ = function(state) {
-                this.version = state[0];
-                this.shape = state[1];
-                this.typecode = state[2];
-                this.is_f_order = state[3];
-                this.rawdata = state[4];
-            };
-            this.__read__ = function(unpickler) {
-                const array = {};
-                sklearn.Utility.applyType(array, this.subtype);
-                array.dtype = this.typecode;
-                array.shape = this.shape;
-                const dims = array.shape && array.shape.length > 0 ? array.shape.reduce((a, b) => a * b) : 1;
-                const size = array.dtype.itemsize * dims;
-                if (typeof this.rawdata == 'string') {
-                    array.data = unpickler.unescape(this.rawdata, size);
-                    if (array.data.length != size) {
-                        throw new sklearn.Error('Invalid string array data size.');
-                    }
-                }
-                else {
-                    array.data = this.rawdata;
-                }
-                return array;
-            };
-        };
-        constructorTable['pathlib.PosixPath'] = function() {
-            this.path = Array.from(arguments).join('/');
-        };
-        constructorTable['sklearn.calibration._CalibratedClassifier'] = function() {};
-        constructorTable['sklearn.calibration._SigmoidCalibration'] = function() {};
-        constructorTable['sklearn.calibration.CalibratedClassifierCV'] = function() {};
-        constructorTable['sklearn.compose._column_transformer.ColumnTransformer'] = function() {};
-        constructorTable['sklearn.compose._target.TransformedTargetRegressor'] = function() {};
-        constructorTable['sklearn.cluster._dbscan.DBSCAN'] = function() {};
-        constructorTable['sklearn.cluster._kmeans.KMeans'] = function() {};
-        constructorTable['sklearn.decomposition._pca.PCA'] = function() {};
-        constructorTable['sklearn.decomposition.PCA'] = function() {};
-        constructorTable['sklearn.decomposition.pca.PCA'] = function() {};
-        constructorTable['sklearn.decomposition._truncated_svd.TruncatedSVD'] = function() {};
-        constructorTable['sklearn.decomposition.truncated_svd.TruncatedSVD'] = function() {};
-        constructorTable['sklearn.discriminant_analysis.LinearDiscriminantAnalysis'] = function() {};
-        constructorTable['sklearn.dummy.DummyClassifier'] = function() {};
-        constructorTable['sklearn.externals.joblib.numpy_pickle.NumpyArrayWrapper'] = constructorTable['joblib.numpy_pickle.NumpyArrayWrapper'];
-        constructorTable['sklearn.externals.joblib.numpy_pickle.NDArrayWrapper'] = function() {};
-        constructorTable['sklearn.ensemble._bagging.BaggingClassifier'] = function() {};
-        constructorTable['sklearn.ensemble._forest.RandomForestRegressor'] = function() {};
-        constructorTable['sklearn.ensemble._forest.RandomForestClassifier'] = function() {};
-        constructorTable['sklearn.ensemble._forest.ExtraTreesClassifier'] = function() {};
-        constructorTable['sklearn.ensemble._gb_losses.BinomialDeviance'] = function() {};
-        constructorTable['sklearn.ensemble._gb_losses.LeastSquaresError'] = function() {};
-        constructorTable['sklearn.ensemble._gb_losses.MultinomialDeviance'] = function() {};
-        constructorTable['sklearn.ensemble._gb.GradientBoostingClassifier'] = function() {};
-        constructorTable['sklearn.ensemble._gb.GradientBoostingRegressor'] = function() {};
-        constructorTable['sklearn.ensemble._iforest.IsolationForest'] = function() {};
-        constructorTable['sklearn.ensemble._voting.VotingClassifier'] = function() {};
-        constructorTable['sklearn.ensemble.forest.RandomForestClassifier'] = function() {};
-        constructorTable['sklearn.ensemble.forest.RandomForestRegressor'] = function() {};
-        constructorTable['sklearn.ensemble.forest.ExtraTreesClassifier'] = function() {};
-        constructorTable['sklearn.ensemble.gradient_boosting.BinomialDeviance'] = function() {};
-        constructorTable['sklearn.ensemble.gradient_boosting.GradientBoostingClassifier'] = function() {};
-        constructorTable['sklearn.ensemble.gradient_boosting.LogOddsEstimator'] = function() {};
-        constructorTable['sklearn.ensemble.gradient_boosting.MultinomialDeviance'] = function() {};
-        constructorTable['sklearn.ensemble.gradient_boosting.PriorProbabilityEstimator'] = function() {};
-        constructorTable['sklearn.ensemble.weight_boosting.AdaBoostClassifier'] = function() {};
-        constructorTable['sklearn.feature_extraction._hashing.FeatureHasher'] = function() {};
-        constructorTable['sklearn.feature_extraction.text.CountVectorizer'] = function() {};
-        constructorTable['sklearn.feature_extraction.text.HashingVectorizer'] = function() {};
-        constructorTable['sklearn.feature_extraction.text.TfidfTransformer'] = function() {};
-        constructorTable['sklearn.feature_extraction.text.TfidfVectorizer'] = function() {};
-        constructorTable['sklearn.feature_selection._from_model.SelectFromModel'] = function() {};
-        constructorTable['sklearn.feature_selection._univariate_selection.SelectKBest'] = function() {};
-        constructorTable['sklearn.feature_selection._univariate_selection.SelectPercentile'] = function() {};
-        constructorTable['sklearn.feature_selection._variance_threshold.VarianceThreshold'] = function() {};
-        constructorTable['sklearn.feature_selection.univariate_selection.SelectKBest'] = function() {};
-        constructorTable['sklearn.feature_selection.variance_threshold.VarianceThreshold'] = function() {};
-        constructorTable['sklearn.impute._base.SimpleImputer'] = function() {};
-        constructorTable['sklearn.impute.SimpleImputer'] = function() {};
-        constructorTable['sklearn.isotonic.IsotonicRegression'] = function() {};
-        constructorTable['sklearn.linear_model._base.LinearRegression'] = function() {};
-        constructorTable['sklearn.linear_model._coordinate_descent.ElasticNet'] = function() {};
-        constructorTable['sklearn.linear_model._logistic.LogisticRegression'] = function() {};
-        constructorTable['sklearn.linear_model._sgd_fast.ModifiedHuber'] = function() {};
-        constructorTable['sklearn.linear_model._sgd_fast.SquaredHinge'] = function() {};
-        constructorTable['sklearn.linear_model._stochastic_gradient.SGDClassifier'] = function() {};
-        constructorTable['sklearn.linear_model.base.LinearRegression'] = function() {};
-        constructorTable['sklearn.linear_model.sgd_fast.Hinge'] = function() {};
-        constructorTable['sklearn.linear_model.LogisticRegression'] = function() {};
-        constructorTable['sklearn.linear_model.logistic.LogisticRegression'] = function() {};
-        constructorTable['sklearn.linear_model.logistic.LogisticRegressionCV'] = function() {};
-        constructorTable['sklearn.linear_model.LassoLars​'] = function() {};
-        constructorTable['sklearn.linear_model.ridge.Ridge'] = function() {};
-        constructorTable['sklearn.linear_model.sgd_fast.Log'] = function() {};
-        constructorTable['sklearn.linear_model.stochastic_gradient.SGDClassifier'] = function() {};
-        constructorTable['sklearn.metrics._scorer._PredictScorer'] = function() {};
-        constructorTable['sklearn.metrics.scorer._PredictScorer'] = function() {};
-        constructorTable['sklearn.model_selection._search.GridSearchCV'] = function() {};
-        constructorTable['sklearn.model_selection._search.RandomizedSearchCV'] = function() {};
-        constructorTable['sklearn.multiclass.OneVsRestClassifier'] = function() {};
-        constructorTable['sklearn.multioutput.MultiOutputRegressor'] = function() {};
-        constructorTable['sklearn.naive_bayes.BernoulliNB'] = function() {};
-        constructorTable['sklearn.naive_bayes.ComplementNB'] = function() {};
-        constructorTable['sklearn.naive_bayes.GaussianNB'] = function() {};
-        constructorTable['sklearn.naive_bayes.MultinomialNB'] = function() {};
-        constructorTable['sklearn.neighbors._classification.KNeighborsClassifier'] = function() {};
-        constructorTable['sklearn.neighbors._dist_metrics.newObj'] = function() {};
-        constructorTable['sklearn.neighbors._kd_tree.newObj'] = function() {};
-        constructorTable['sklearn.neighbors._regression.KNeighborsRegressor'] = function() {};
-        constructorTable['sklearn.neighbors.classification.KNeighborsClassifier'] = function() {};
-        constructorTable['sklearn.neighbors.dist_metrics.newObj'] = function() {};
-        constructorTable['sklearn.neighbors.kd_tree.newObj'] = function() {};
-        constructorTable['sklearn.neighbors.KNeighborsClassifier'] = function() {};
-        constructorTable['sklearn.neighbors.KNeighborsRegressor'] = function() {};
-        constructorTable['sklearn.neighbors.regression.KNeighborsRegressor'] = function() {};
-        constructorTable['sklearn.neighbors.unsupervised.NearestNeighbors'] = function() {};
-        constructorTable['sklearn.neural_network._multilayer_perceptron.MLPClassifier'] = function() {};
-        constructorTable['sklearn.neural_network._multilayer_perceptron.MLPRegressor'] = function() {};
-        constructorTable['sklearn.neural_network._stochastic_optimizers.AdamOptimizer'] = function() {};
-        constructorTable['sklearn.neural_network._stochastic_optimizers.SGDOptimizer'] = function() {};
-        constructorTable['sklearn.neural_network.rbm.BernoulliRBM'] = function() {};
-        constructorTable['sklearn.neural_network.multilayer_perceptron.MLPClassifier'] = function() {};
-        constructorTable['sklearn.neural_network.multilayer_perceptron.MLPRegressor'] = function() {};
-        constructorTable['sklearn.neural_network.stochastic_gradient.SGDClassifier'] = function() {};
-        constructorTable['sklearn.pipeline.Pipeline'] = function() {};
-        constructorTable['sklearn.pipeline.FeatureUnion'] = function() {};
-        constructorTable['sklearn.preprocessing._data.MinMaxScaler'] = function() {};
-        constructorTable['sklearn.preprocessing._data.MaxAbsScaler'] = function() {};
-        constructorTable['sklearn.preprocessing._data.Normalizer'] = function() {};
-        constructorTable['sklearn.preprocessing._data.PolynomialFeatures'] = function() {};
-        constructorTable['sklearn.preprocessing._data.QuantileTransformer'] = function() {};
-        constructorTable['sklearn.preprocessing._data.RobustScaler'] = function() {};
-        constructorTable['sklearn.preprocessing._data.StandardScaler'] = function() {};
-        constructorTable['sklearn.preprocessing._discretization.KBinsDiscretizer'] = function() {};
-        constructorTable['sklearn.preprocessing._encoders.OneHotEncoder'] = function() {};
-        constructorTable['sklearn.preprocessing._function_transformer.FunctionTransformer'] = function() {};
-        constructorTable['sklearn.preprocessing._label.LabelBinarizer'] = function() {};
-        constructorTable['sklearn.preprocessing._label.LabelEncoder'] = function() {};
-        constructorTable['sklearn.preprocessing.data.Binarizer'] = function() {};
-        constructorTable['sklearn.preprocessing.data.MaxAbsScaler'] = function() {};
-        constructorTable['sklearn.preprocessing.data.MinMaxScaler'] = function() {};
-        constructorTable['sklearn.preprocessing.data.Normalizer'] = function() {};
-        constructorTable['sklearn.preprocessing.data.OneHotEncoder'] = function() {};
-        constructorTable['sklearn.preprocessing.data.PolynomialFeatures'] = function() {};
-        constructorTable['sklearn.preprocessing.data.PowerTransformer'] = function() {};
-        constructorTable['sklearn.preprocessing.data.RobustScaler'] = function() {};
-        constructorTable['sklearn.preprocessing.data.QuantileTransformer'] = function() {};
-        constructorTable['sklearn.preprocessing.data.StandardScaler'] = function() {};
-        constructorTable['sklearn.preprocessing.imputation.Imputer'] = function() {};
-        constructorTable['sklearn.preprocessing.label.LabelBinarizer'] = function() {};
-        constructorTable['sklearn.preprocessing.label.LabelEncoder'] = function() {};
-        constructorTable['sklearn.preprocessing.label.MultiLabelBinarizer'] = function() {};
-        constructorTable['sklearn.svm._classes.SVC'] = function() {};
-        constructorTable['sklearn.svm._classes.SVR'] = function() {};
-        constructorTable['sklearn.svm.classes.LinearSVC'] = function() {};
-        constructorTable['sklearn.svm.classes.SVC'] = function() {};
-        constructorTable['sklearn.svm.classes.SVR'] = function() {};
-        constructorTable['sklearn.tree._classes.DecisionTreeClassifier'] = function() {};
-        constructorTable['sklearn.tree._classes.DecisionTreeRegressor'] = function() {};
-        constructorTable['sklearn.tree._classes.ExtraTreeClassifier'] = function() {};
-        constructorTable['sklearn.tree._classes.ExtraTreeRegressor'] = function() {};
-        constructorTable['sklearn.tree._tree.Tree'] = function(n_features, n_classes, n_outputs) {
-            this.n_features = n_features;
-            this.n_classes = n_classes;
-            this.n_outputs = n_outputs;
-            this.__setstate__ = function(state) {
-                this.max_depth = state.max_depth;
-                this.node_count = state.node_count;
-                this.nodes = state.nodes;
-                this.values = state.values;
-            };
-        };
-        constructorTable['sklearn.tree.tree.DecisionTreeClassifier'] = function() {};
-        constructorTable['sklearn.tree.tree.DecisionTreeRegressor'] = function() {};
-        constructorTable['sklearn.tree.tree.ExtraTreeClassifier'] = function() {};
-        constructorTable['sklearn.utils.deprecation.DeprecationDict'] = function() {};
-        constructorTable['re.Pattern'] = function(pattern, flags) {
-            this.pattern = pattern;
-            this.flags = flags;
-        };
-        constructorTable['spacy._ml.PrecomputableAffine'] = function() {
-            this.__setstate__ = function(state) {
-                Object.assign(this, new pickle.Unpickler(state).load(function_call, null));
-            };
-        };
-        constructorTable['spacy.syntax._parser_model.ParserModel'] = function() {
-            this.__setstate__ = function(state) {
-                Object.assign(this, new pickle.Unpickler(state).load(function_call, null));
-            };
-        };
-        constructorTable['thinc.describe.Biases'] = function() {
-            this.__setstate__ = function(state) {
-                Object.assign(this, state);
-            };
-        };
-        constructorTable['thinc.describe.Dimension'] = function() {
-            this.__setstate__ = function(state) {
-                Object.assign(this, state);
-            };
-        };
-        constructorTable['thinc.describe.Gradient'] = function() {
-            this.__setstate__ = function(state) {
-                Object.assign(this, state);
-            };
-        };
-        constructorTable['thinc.describe.Weights'] = function() {
-            this.__setstate__ = function(state) {
-                Object.assign(this, state);
-            };
-        };
-        constructorTable['thinc.describe.Synapses'] = function() {
-            this.__setstate__ = function(state) {
-                Object.assign(this, state);
-            };
-        };
-        constructorTable['thinc.neural._classes.affine.Affine'] = function() {
-            this.__setstate__ = function(state) {
-                Object.assign(this, new pickle.Unpickler(state).load(function_call, null));
-            };
-        };
-        constructorTable['thinc.neural._classes.convolution.ExtractWindow'] = function() {
-            this.__setstate__ = function(state) {
-                Object.assign(this, new pickle.Unpickler(state).load(function_call, null));
-            };
-        };
-        constructorTable['thinc.neural._classes.feature_extracter.FeatureExtracter'] = function() {
-            this.__setstate__ = function(state) {
-                Object.assign(this, new pickle.Unpickler(state).load(function_call, null));
-            };
-        };
-        constructorTable['thinc.neural._classes.feed_forward.FeedForward'] = function() {
-            this.__setstate__ = function(state) {
-                Object.assign(this, new pickle.Unpickler(state).load(function_call, null));
-            };
-        };
-        constructorTable['thinc.neural._classes.function_layer.FunctionLayer'] = function() {
-            this.__setstate__ = function(state) {
-                Object.assign(this, new pickle.Unpickler(state).load(function_call, null));
-            };
-        };
-        constructorTable['thinc.neural._classes.hash_embed.HashEmbed'] = function() {
-            this.__setstate__ = function(state) {
-                Object.assign(this, new pickle.Unpickler(state).load(function_call, null));
-            };
-        };
-        constructorTable['thinc.neural._classes.layernorm.LayerNorm'] = function() {
-            this.__setstate__ = function(state) {
-                Object.assign(this, new pickle.Unpickler(state).load(function_call, null));
-            };
-        };
-        constructorTable['thinc.neural._classes.maxout.Maxout'] = function() {
-            this.__setstate__ = function(state) {
-                Object.assign(this, new pickle.Unpickler(state).load(function_call, null));
-            };
-        };
-        constructorTable['thinc.neural._classes.resnet.Residual'] = function() {
-            this.__setstate__ = function(state) {
-                Object.assign(this, new pickle.Unpickler(state).load(function_call, null));
-            };
-        };
-        constructorTable['thinc.neural._classes.softmax.Softmax'] = function() {
-            this.__setstate__ = function(state) {
-                Object.assign(this, new pickle.Unpickler(state).load(function_call, null));
-            };
-        };
-        constructorTable['thinc.neural.mem.Memory'] = function() {
-        };
-        constructorTable['thinc.neural.ops.NumpyOps'] = function() {
-        };
-        constructorTable['types.CodeType'] = function(/* args */) {
-        };
-        constructorTable['types.MethodType'] = function(/* args */) {
-        };
-        constructorTable['xgboost.compat.XGBoostLabelEncoder'] = function() {};
-        constructorTable['xgboost.core.Booster'] = function() {};
-        constructorTable['xgboost.sklearn.XGBClassifier'] = function() {};
-        constructorTable['xgboost.sklearn.XGBRegressor'] = function() {};
-
-        functionTable['__builtin__.bytearray'] = function(source, encoding /*, errors */) {
-            if (source) {
-                if (encoding === 'latin-1') {
-                    const array = new Uint8Array(source.length);
-                    for (let i = 0; i < source.length; i++) {
-                        array[i] = source.charCodeAt(i);
-                    }
-                    return array;
-                }
-                throw new sklearn.Error("Unsupported bytearray encoding '" + JSON.stringify(encoding) + "'.");
-            }
-            return [];
-        };
-        functionTable['__builtin__.bytes'] = function(source, encoding /*, errors */) {
-            if (source) {
-                if (encoding === 'latin-1') {
-                    const array = new Uint8Array(source.length);
-                    for (let i = 0; i < source.length; i++) {
-                        array[i] = source.charCodeAt(i);
-                    }
-                    return array;
-                }
-                throw new sklearn.Error("Unsupported bytearray encoding '" + JSON.stringify(encoding) + "'.");
-            }
-            return [];
-        };
-        functionTable['__builtin__.set'] = function(iterable) {
-            return iterable ? iterable : [];
-        };
-        functionTable['__builtin__.frozenset'] = function(iterable) {
-            return iterable ? iterable : [];
-        };
-        functionTable['__builtin__.getattr'] = function(obj, name, defaultValue) {
-            if (Object.prototype.hasOwnProperty.call(obj, name)) {
-                return obj[name];
-            }
-            return defaultValue;
-        };
-        functionTable['_codecs.encode'] = function(obj /*, econding */) {
-            return obj;
-        };
-        functionTable['builtins.bytearray'] = function(data) {
-            return { data: data };
-        };
-        functionTable['builtins.getattr'] = function(obj, name, defaultValue) {
-            if (Object.prototype.hasOwnProperty.call(obj, name)) {
-                return obj[name];
-            }
-            return defaultValue;
-        };
-        functionTable['builtins.set'] = function(iterable) {
-            return iterable ? iterable : [];
-        };
-        functionTable['builtins.slice'] = function(start, stop, step) {
-            return { start: start, stop: stop, step: step };
-        };
-        functionTable['cloudpickle.cloudpickle._builtin_type'] = function(name) {
-            return name;
-        };
-        functionTable['collections.Counter'] = function(/* iterable */) {
-            return { __module__: 'collections', __name__: 'Counter' };
-        };
-        functionTable['collections.defaultdict'] = function(/* default_factory */) {
-            return {};
-        };
-        functionTable['collections.OrderedDict'] = function(args) {
-            const obj = new Map();
-            obj.__setitem__ = function(key, value) {
-                obj.set(key, value);
-            };
-            if (args) {
-                for (const arg of args) {
-                    obj.__setitem__(arg[0], arg[1]);
-                }
-            }
-            return obj;
-        };
-        functionTable['copy_reg._reconstructor'] = function(cls, base, state) {
-            if (base == '__builtin__.object') {
-                const obj = {};
-                sklearn.Utility.applyType(obj, cls);
-                return obj;
-            }
-            if (base == '__builtin__.tuple') {
-                return state;
-            }
-            throw new sklearn.Error("Unknown base type '" + base + "'.");
-        };
-        functionTable['dill._dill._create_cell'] = function(/* args */) {
-            return function() {
-                // TODO
-            };
-        };
-        functionTable['dill._dill._create_code'] = function(args) {
-            return function_call('types.CodeType', [ args ]);
-        };
-        functionTable['dill._dill._create_function'] = function(/* fcode, fglobals, fname, fdefaults, fclosure, fdict, fkwdefaults */) {
-            return function() {
-                // TODO
-            };
-        };
-        functionTable['dill._dill._get_attr'] = function(self, name) {
-            if (Object.prototype.hasOwnProperty.call(self, name)) {
-                return self[name];
-            }
-            return undefined;
-        };
-        functionTable['dill._dill._load_type'] = function(name) {
-            return constructorTable['types.' + name];
-        };
-        functionTable['numpy.core.multiarray.scalar'] = function(dtype, rawData) {
-            let data = rawData;
-            if (typeof rawData === 'string' || rawData instanceof String) {
-                data = new Uint8Array(rawData.length);
-                for (let i = 0; i < rawData.length; i++) {
-                    data[i] = rawData.charCodeAt(i);
-                }
-            }
-            const dataView = new DataView(data.buffer, data.byteOffset, data.byteLength);
-            switch (dtype.name) {
-                case 'uint8':
-                    return dataView.getUint8(0);
-                case 'float32':
-                    return dataView.getFloat32(0, true);
-                case 'float64':
-                    return dataView.getFloat64(0, true);
-                case 'int8':
-                    return dataView.getInt8(0, true);
-                case 'int16':
-                    return dataView.getInt16(0, true);
-                case 'int32':
-                    return dataView.getInt32(0, true);
-                case 'int64':
-                    return dataView.getInt64(0, true);
-            }
-            throw new sklearn.Error("Unknown scalar type '" + dtype.name + "'.");
-        };
-        functionTable['numpy.ma.core._mareconstruct'] = function(subtype /* , baseclass, baseshape, basetype */) {
-            // _data = ndarray.__new__(baseclass, baseshape, basetype)
-            // _mask = ndarray.__new__(ndarray, baseshape, make_mask_descr(basetype))
-            // return subtype.__new__(subtype, _data, mask=_mask, dtype=basetype,)
-            const obj = {};
-            sklearn.Utility.applyType(obj, subtype);
-            return obj;
-        };
-        functionTable['numpy.random.__RandomState_ctor'] = function() {
-            return {};
-        };
-        functionTable['numpy.random._pickle.__randomstate_ctor'] = function() {
-            return {};
-        };
-        functionTable['numpy.core.numeric._frombuffer'] = function(/* buf, dtype, shape, order */) {
-            return {};
-        };
-        functionTable['re._compile'] = function(pattern, flags) {
-            return function_call('re.Pattern', [ pattern, flags ]);
-        };
-        functionTable['srsly.cloudpickle.cloudpickle._builtin_type'] = function(name) {
-            return function() {
-                return function_call('types.' + name, arguments);
-            };
-        };
-
-        const unknownNameMap = new Set();
-        const knownPackageMap = new Set([
-            'sklearn', 'collections', '__builtin__', 'builtins',
-            'copy_reg', 'gensim', 'joblib','xgboost', 'lightgbm', 'nolearn', 'numpy'
-        ]);
-
-        const function_call = (name, args) => {
-            const func = name instanceof Function ? name : functionTable[name];
-            if (func) {
-                return func.apply(null, args);
-            }
-            const obj = {};
-            sklearn.Utility.applyType(obj, name);
-            const constructor = constructorTable[name];
-            if (constructor) {
-                constructor.apply(obj, args);
-            }
-            else if (name && !unknownNameMap.has(name)) {
-                unknownNameMap.add(name);
-                if (knownPackageMap.has(name.split('.').shift())) {
-                    exception(new sklearn.Error("Unknown function '" + name + "'."), false);
-                }
-            }
-            return obj;
-        };
-
-        const obj = unpickler.load(function_call, () => {});
-        const weights = sklearn.Utility.findWeights(obj);
+        const execution = new python.Execution(null, exception);
+        const unpickler = new python.Unpickler(buffer);
+        const obj = unpickler.load((name, args) => execution.invoke(name, args));
+        const weights = sklearn.Container.findWeights(obj);
         if (weights) {
             this._format = 'NumPy Weights';
             this._type = 'weights';
@@ -1291,14 +693,11 @@ sklearn.Container = class {
                         if (obj.__module__.startsWith('sklearn.')) {
                             return 'scikit-learn' + (obj._sklearn_version ? ' v' + obj._sklearn_version.toString() : '');
                         }
-                        else if (obj.__module__.startsWith('xgboost.')) {
-                            return 'XGBoost' + (obj._sklearn_version ? ' v' + obj._sklearn_version.toString() : '');
-                        }
-                        else if (obj.__module__.startsWith('lightgbm.')) {
-                            return 'LightGBM Pickle';
+                        else if (obj.__module__ === 'xgboost.sklearn') {
+                            return 'scikit-learn XGBoost' + (obj._sklearn_version ? ' v' + obj._sklearn_version.toString() : '');
                         }
-                        else if (obj.__module__.startsWith('nolearn.lasagne.')) {
-                            return 'Lasagne';
+                        else if (obj.__module__ === 'lightgbm.sklearn') {
+                            return 'scikit-learn LightGBM';
                         }
                         else if (obj.__module__.startsWith('gensim.')) {
                             return 'gensim';
@@ -1329,21 +728,6 @@ sklearn.Container = class {
     get data() {
         return this._data;
     }
-};
-
-sklearn.Utility = class {
-
-    static isTensor(obj) {
-        return obj && obj.__module__ === 'numpy' && obj.__name__ === 'ndarray';
-    }
-
-    static applyType(obj, name){
-        if (name) {
-            const parts = name.split('.');
-            obj.__name__ = parts.pop();
-            obj.__module__ = parts.join('.');
-        }
-    }
 
     static findWeights(obj) {
         const keys = [ '', 'blobs' ];
@@ -1392,6 +776,13 @@ sklearn.Utility = class {
     }
 };
 
+sklearn.Utility = class {
+
+    static isTensor(obj) {
+        return obj && obj.__module__ === 'numpy' && obj.__name__ === 'ndarray';
+    }
+};
+
 sklearn.Error = class extends Error {
 
     constructor(message) {

+ 8 - 4
source/tar.js

@@ -4,12 +4,16 @@ var tar = tar || {};
 
 tar.Archive = class {
 
-    constructor(buffer) {
-        this._entries = [];
+    static open(buffer) {
         const stream = buffer instanceof Uint8Array ? new tar.BinaryReader(buffer) : buffer;
-        if (stream.length < 512) {
-            throw new tar.Error('Invalid tar archive size.');
+        if (stream.length > 512) {
+            return new tar.Archive(stream);
         }
+        throw new tar.Error('Invalid tar archive size.');
+    }
+
+    constructor(stream) {
+        this._entries = [];
         while (stream.position < stream.length) {
             this._entries.push(new tar.Entry(stream));
             if (stream.position + 512 > stream.length ||

+ 1 - 3
source/tf.js

@@ -133,9 +133,7 @@ tf.ModelFactory = class {
             }
             else if (extension === 'json') {
                 try {
-                    const buffer = context.stream.peek();
-                    const reader = json.TextReader.create(buffer);
-                    const root = reader.read();
+                    const root = context.tags('json').get('');
                     const graph_def = new tf.proto.GraphDef();
                     const meta_graph = new tf.proto.MetaGraphDef();
                     meta_graph.graph_def = graph_def;

+ 27 - 4
source/view.js

@@ -8,6 +8,7 @@ var gzip = gzip || require('./gzip');
 var tar = tar || require('./tar');
 var json = json || require('./json');
 var protobuf = protobuf || require('./protobuf');
+var python = python || require('./python');
 
 var d3 = d3 || require('d3');
 var dagre = dagre || require('dagre');
@@ -746,7 +747,7 @@ view.View = class {
                             let text = '';
                             const type = tuple.from.type;
                             if (type && type.shape && type.shape.dimensions && type.shape.dimensions.length > 0) {
-                                text = type.shape.dimensions.join('\u00D7');
+                                text = type.shape.dimensions.map((dimension) => dimension || '?').join('\u00D7');
                             }
 
                             if (this._showNames) {
@@ -1131,6 +1132,7 @@ view.ModelContext = class {
                     case 'json': {
                         const reader = json.TextReader.create(this.stream.peek());
                         const obj = reader.read();
+                        tags.set('', obj);
                         if (!Array.isArray(obj)) {
                             for (const key in obj) {
                                 tags.set(key, key === 'format' && obj[key] === 'graph-model' ? obj[key] : true);
@@ -1143,11 +1145,31 @@ view.ModelContext = class {
                                 }
                             }
                         }
+                        break;
+                    }
+                    case 'pkl': {
+                        if (this.stream.length > 2) {
+                            const stream = this.stream.peek(1)[0] === 0x78 ? zip.Archive.open(this.stream).entries[0].stream : this.stream;
+                            const header = stream.peek(2);
+                            if (header[0] === 0x80 && header[1] < 7) {
+                                const unpickler = new python.Unpickler(stream);
+                                const execution = new python.Execution(null, (error, fatal) => {
+                                    const message = error && error.message ? error.message : error.toString();
+                                    this.exception(new view.Error(message.replace(/\.$/, '') + " in '" + this.identifier + "'."), fatal);
+                                });
+                                const data = unpickler.load((name, args) => execution.invoke(name, args));
+                                const name = data && data.__module__ && data.__name__ ? data.__module__ + '.' + data.__name__ : '';
+                                tags.set(name, data);
+                                this.stream.seek(0);
+                            }
+                        }
+                        break;
                     }
                 }
             }
             catch (error) {
                 tags = new Map();
+                this.stream.seek(0);
             }
             this._tags.set(type, tags);
         }
@@ -1226,6 +1248,7 @@ view.ModelFactoryService = class {
         this.register('./tf', [ '.pb', '.meta', '.pbtxt', '.prototxt', '.pt', '.json', '.index', '.ckpt', '.graphdef', /.data-[0-9][0-9][0-9][0-9][0-9]-of-[0-9][0-9][0-9][0-9][0-9]$/, /^events.out.tfevents./ ]);
         this.register('./mediapipe', [ '.pbtxt' ]);
         this.register('./uff', [ '.uff', '.pb', '.pbtxt', '.uff.txt', '.trt', '.engine' ]);
+        this.register('./lasagne', [ '.pkl', '.pickle', '.joblib', '.model', '.pkl.z', '.joblib.z' ]);
         this.register('./sklearn', [ '.pkl', '.pickle', '.joblib', '.model', '.meta', '.pb', '.pt', '.h5', '.pkl.z', '.joblib.z' ]);
         this.register('./cntk', [ '.model', '.cntk', '.cmf', '.dnn' ]);
         this.register('./paddle', [ '.paddle', '.pdmodel', '__model__', '.pbtxt', '.txt', '.tar', '.tar.gz' ]);
@@ -1358,7 +1381,7 @@ view.ModelFactoryService = class {
         try {
             extension = identifier.split('.').pop().toLowerCase();
             if (extension === 'gz' || extension === 'tgz' || (buffer.length >= 18 && buffer[0] === 0x1f && buffer[1] === 0x8b)) {
-                const entries = new gzip.Archive(stream).entries;
+                const entries = gzip.Archive.open(stream).entries;
                 if (entries.length === 1) {
                     const entry = entries[0];
                     if (entry.name) {
@@ -1389,7 +1412,7 @@ view.ModelFactoryService = class {
         try {
             extension = identifier.split('.').pop().toLowerCase();
             if (extension === 'zip' || (buffer.length > 2 && buffer[0] === 0x50 && buffer[1] === 0x4B)) {
-                entries.set('zip', new zip.Archive(stream).entries);
+                entries.set('zip', zip.Archive.open(stream).entries);
             }
             if (extension === 'tar' || (buffer.length >= 512)) {
                 let sum = 0;
@@ -1402,7 +1425,7 @@ view.ModelFactoryService = class {
                 }
                 checksum = parseInt(checksum, 8);
                 if (!isNaN(checksum) && sum === checksum) {
-                    entries.set('tar', new tar.Archive(stream).entries);
+                    entries.set('tar', tar.Archive.open(stream).entries);
                 }
             }
         }

+ 49 - 4
source/zip.js

@@ -1,16 +1,25 @@
 /* jshint esversion: 6 */
 
 var zip = zip || {};
+var zlib = zlib || {};
 
 zip.Archive = class {
 
-    constructor(buffer) {
-        this._entries = [];
+    static open(buffer) {
         const stream = buffer instanceof Uint8Array ? new zip.BinaryReader(buffer) : buffer;
+        if (stream.length > 2 && stream.peek(1)[0] === 0x78) { // zlib
+            return new zlib.Archive(stream);
+        }
         const signature = [ 0x50, 0x4B, 0x01, 0x02 ];
-        if (stream.length < 4 || !stream.peek(2).every((value, index) => value === signature[index])) {
-            throw new zip.Error('Invalid Zip archive.');
+        if (stream.length > 4 && stream.peek(2).every((value, index) => value === signature[index])) {
+            return new zip.Archive(stream);
         }
+        throw new zip.Error('Invalid Zip archive.');
+    }
+
+
+    constructor(stream) {
+        this._entries = [];
         const lookup = (stream, signature) => {
             let position = stream.length;
             do {
@@ -50,6 +59,7 @@ zip.Archive = class {
         stream.seek(offset); // central directory offset
 
         const entries = [];
+        const signature = [ 0x50, 0x4B, 0x01, 0x02 ];
         while (stream.position + 4 < stream.length && stream.read(4).every((value, index) => value === signature[index])) {
             const entry = {};
             const reader = new zip.BinaryReader(stream.read(42));
@@ -506,6 +516,9 @@ zip.InflaterStream = class {
     }
 
     get length() {
+        if (this._length === undefined) {
+            this._inflate();
+        }
         return this._length;
     }
 
@@ -555,6 +568,7 @@ zip.InflaterStream = class {
         if (this._buffer === undefined) {
             const buffer = this._stream.peek();
             this._buffer = new zip.Inflater().inflateRaw(buffer, this._length);
+            this._length = this._buffer.length;
             delete this._stream;
         }
     }
@@ -633,6 +647,37 @@ zip.BinaryReader = class {
     }
 };
 
+zlib.Archive = class {
+
+    constructor(stream) {
+        stream.read(2);
+        this._entries = [ new zlib.Entry(stream) ];
+    }
+
+    get entries() {
+        return this._entries;
+    }
+};
+
+zlib.Entry = class {
+
+    constructor(stream) {
+        this._stream = new zip.InflaterStream(stream);
+    }
+
+    get name() {
+        return '';
+    }
+
+    get stream() {
+        return this._stream;
+    }
+
+    get data() {
+        return this.stream.peek();
+    }
+};
+
 zip.Error = class extends Error {
     constructor(message) {
         super(message);

+ 3 - 3
test/models.js

@@ -342,7 +342,7 @@ function decompress(buffer, identifier) {
     let archive = null;
     const extension = identifier.split('.').pop().toLowerCase();
     if (extension == 'gz' || extension == 'tgz') {
-        archive = new gzip.Archive(buffer);
+        archive = gzip.Archive.open(buffer);
         if (archive.entries.length == 1) {
             const entry = archive.entries[0];
             if (entry.name) {
@@ -360,10 +360,10 @@ function decompress(buffer, identifier) {
 
     switch (identifier.split('.').pop().toLowerCase()) {
         case 'tar':
-            archive = new tar.Archive(buffer);
+            archive = tar.Archive.open(buffer);
             break;
         case 'zip':
-            archive = new zip.Archive(buffer);
+            archive = zip.Archive.open(buffer);
             break;
     }
     return archive;

+ 8 - 8
test/models.json

@@ -2337,6 +2337,13 @@
     "script": "./tools/tf sync install zoo",
     "link":   "https://www.tensorflow.org/api_docs/python/tf/keras/applications"
   },
+  {
+    "type":   "lasagne",
+    "target": "net2.pkl",
+    "source": "https://raw.githubusercontent.com/Aabglov/LasaganeTest/master/results/net2.pkl",
+    "format": "Lasagne",
+    "link":   "https://github.com/Aabglov/LasaganeTest"
+  },
   {
     "type":   "lightgbm",
     "target": "simple_example.txt",
@@ -4845,18 +4852,11 @@
     "format": "scikit-learn v0.19.2",
     "link":   "https://github.com/lutzroeder/netron/issues/182"
   },
-  {
-    "type":   "sklearn",
-    "target": "net2.pkl",
-    "source": "https://raw.githubusercontent.com/Aabglov/LasaganeTest/master/results/net2.pkl",
-    "format": "Lasagne",
-    "link":   "https://github.com/Aabglov/LasaganeTest"
-  },
   {
     "type":   "sklearn",
     "target": "pima.xgboost.joblib.pkl",
     "source": "https://github.com/lutzroeder/netron/files/2656501/pima.xgboost.joblib.pkl.zip[pima.xgboost.joblib.pkl]",
-    "format": "XGBoost",
+    "format": "scikit-learn XGBoost",
     "link":   "https://github.com/lutzroeder/netron/issues/182"
   },
   {

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