Пример #1
0
    def __init__(self, inputlayers, outputlayers):
        """
        :type inputlayers: list
        :param inputlayers: List of Lasagne input layers.

        :type outputlayers: list
        :param outputlayers: List of output layers.
        """
        # Init superclass
        super(lasagnelayer, self).__init__()
        # Make sure lasagne is available
        assert las is not None, "Lasagne could not be imported."

        # Meta
        self.inputlayers = pyk.delist(inputlayers)
        self.outputlayers = pyk.delist(outputlayers)

        # Get inpshape from lasagne
        self.lasinpshape = pyk.delist(
            [list(il.shape) for il in pyk.obj2list(self.inputlayers)])

        # Parse layer info
        parsey = netutils.parselayerinfo(dim=2,
                                         allowsequences=True,
                                         numinp=pyk.smartlen(inputlayers),
                                         issequence=False,
                                         inpshape=self.lasinpshape)

        self.dim = parsey['dim']
        self.inpdim = parsey['inpdim']
        self.allowsequences = parsey['allowsequences']
        self.issequence = parsey['issequence']
        self.numinp = parsey['numinp']

        # Read parameters
        self._params = las.layers.get_all_params(self.outputlayers)
        self._cparams = [
            netutils.getshared(like=param, value=1.) for param in self.params
        ]
        # Name parameters right
        self.nameparams()

        # Shape inference
        self.inpshape = parsey['inpshape']

        # Check numout for consistency
        assert self.numout == pyk.smartlen(self.outputlayers), "Number of outputs doesn't match " \
                                                               "the given number of output-layers"

        self.x, self.y = netutils.makelayerxy(self.inpdim, self.outdim,
                                              id(self))
Пример #2
0
    def func(*args, **kwargs):
        # Compute argument length
        arglen = max([pyk.smartlen(arg) for arg in list(args) + list(kwargs.values())])

        # Convert arguments to list
        args = [pyk.obj2list(arg) if not len(pyk.obj2list(arg)) == 1 else pyk.obj2list(arg)*arglen for arg in args]
        kwargs = {kw: pyk.obj2list(arg) if not len(pyk.obj2list(arg)) == 1 else pyk.obj2list(arg)*arglen
                  for kw, arg in kwargs.items()}

        # Make sure list sizes check out
        assert all([pyk.smartlen(arg) == arglen for arg in args]) if pyk.smartlen(arg) != 0 else True, \
            "Input lists must all have the same length."
        assert all([pyk.smartlen(kwarg) == pyk.smartlen(kwargs.values()[0]) == arglen for kwarg in kwargs.values()]) \
            if pyk.smartlen(kwargs) != 0 else True, "Keyword argument vectors must have the same length (= argument " \
                                                    "vector length)"

        # Run the loop (can be done with a long-ass list comprehension, but I don't see the point)
        res = []
        if not len(kwargs) == 0 and not len(args) == 0:
            for arg, kwarg in zip(zip(*args), zip(*kwargs.values())):
                res.append(fun(*arg, **{kw: kwa for kw, kwa in zip(kwargs.keys(), kwarg)}))
        else:
            if len(kwargs) == 0:
                res = [fun(*arg) for arg in zip(*args)]
            elif len(args) == 0:
                res = [fun(**{kw: kwa for kw, kwa in zip(kwargs.keys(), kwarg)}) for kwarg in zip(*kwargs.values())]
            else:
                return []

        # Return results
        return pyk.delist(res)
Пример #3
0
    def __call__(self, *args, **kwargs):
        # This function wraps the compiled theano function.
        # ------------------------------------------------------
        # Don't allow args if self.inputs is a dictionary. This is because the user can not be expected to know
        # exactly how a dictionary is ordered.
        args = list(args)

        if isinstance(self.inputs, dict):
            assert args == [], "Antipasti function object expects keyword arguments because the " \
                               "provided input was a dict."
        if isinstance(self.inputs, list):
            assert kwargs == {}, "Keywords could not be parsed by the Antipasti function object."

        # Flatten kwargs or args
        if args:
            funcargs = list(pyk.flatten(args))
        else:
            funcargs = list(pyk.flatten(kwargs.values()))

        # Evaluate function
        outlist = pyk.obj2list(self._thfunction(*funcargs), ndarray2list=False)

        # Parse output list
        expoutputs = self.outputs.values() if isinstance(
            self.outputs, dict) else self.outputs
        expoutputs = pyk.obj2list(expoutputs, ndarray2list=False)

        # Make sure the theano function has returned the correct number of outputs
        assert len(outlist) == len(list(pyk.flatten(expoutputs))), "Number of outputs returned by the theano function " \
                                                                   "is not consistent with the number of expected " \
                                                                   "outputs."

        # Unflatten theano function output (outlist)
        # Get list with sublist lengths
        lenlist = [pyk.smartlen(expoutput) for expoutput in expoutputs]
        # Unflatten outlist
        outputs = pyk.unflatten(outlist, lenlist)

        # Write to dictionary if self.outputs is a dictionary
        if isinstance(self.outputs, dict):
            outputs = {
                outname: outvar
                for outname, outvar in zip(self.outputs.keys(), outputs)
            }
        elif isinstance(self.outputs, list):
            outputs = tuple(outputs)
        else:
            outputs = pyk.delist(outputs)

        return outputs
Пример #4
0
    def feedforward(self, inp=None):
        if inp is None:
            inp = self.x
        else:
            self.x = inp

        # Loop over connections, merge and append to a buffer
        out = []
        for connum, conn in enumerate(self.connections):
            if pyk.smartlen(conn) == 1:
                out.append(inp[pyk.delist(pyk.obj2list(conn))])
            else:
                if self.merge:
                    out.append(self.mergelayers[connum].feedforward(inp=[inp[node] for node in conn]))
                else:
                    out.append([inp[node] for node in conn])

        self.y = out
        return self.y
Пример #5
0
    def feedforward(self, inp=None):
        if inp is None:
            inp = self.x
        else:
            self.x = inp

        # Loop over connections, merge and append to a buffer
        out = []
        for connum, conn in enumerate(self.connections):
            if pyk.smartlen(conn) == 1:
                out.append(inp[pyk.delist(pyk.obj2list(conn))])
            else:
                if self.merge:
                    out.append(self.mergelayers[connum].feedforward(
                        inp=[inp[node] for node in conn]))
                else:
                    out.append([inp[node] for node in conn])

        self.y = out
        return self.y
Пример #6
0
    def __init__(self,
                 connections,
                 merge=True,
                 dim=2,
                 issequence=False,
                 inpshape=None):
        """
        :type connections: list
        :param connections: List of connections. E.g.: [[0, 1], 1, [0, 1, 2]] would merge inputs 0 & 1 and write to
                            first output slot, 1 to second output slot and 0, 1 & 2 merged to the third output slot.
        :param dim:
        :param issequence:
        :param inpshape:
        :return:
        """
        super(circuitlayer, self).__init__()

        # Compute the number of i/o slots
        self.numinp = len(pyk.unique(list(pyk.flatten(connections))))
        self.numout = len(connections)
        self.merge = bool(merge)

        # TODO Fix this
        assert self.numinp != 1, "Circuit layer must have atleast 2 inputs. Consider using a replicatelayer."

        # Parse
        dim = [
            dim,
        ] * self.numinp if isinstance(dim,
                                      int) and self.numinp is not None else dim
        issequence = [issequence, ] * self.numinp if isinstance(issequence, bool) and self.numinp is not None \
            else issequence

        # Parse inpshape if numinp, dim, issequence is given
        if self.numinp is not None and dim is not None and inpshape is None:
            if 3 in dim:
                # issequence doesn't matter, inpshape can still be filled
                inpshape = [[
                    None,
                ] * 5 for _ in dim]
            else:
                # dim is 2, but input might still be sequential
                if issequence is None:
                    pass
                else:
                    if issequence:
                        # issequence is false and dim = 2, ergo 2D data
                        inpshape = [[
                            None,
                        ] * 4 for _ in dim]
                    else:
                        # issequence is true, ergo 2D sequential data
                        inpshape = [[
                            None,
                        ] * 5 for _ in dim]

        # Meta
        self.connections = connections
        self.dim = dim if dim is not None else [{
            4: 2,
            5: 2
        }[len(ishp)] for ishp in inpshape]
        self.allowsequences = True
        self.issequence = [idim == 2 and len(ishp) == 5 for idim, ishp in zip(self.dim, inpshape)] \
            if issequence is None else issequence
        self.inpdim = [
            len(ishp) if ishp is not None else 5 if iisseq else {
                2: 4,
                3: 5
            }[idim]
            for ishp, iisseq, idim in zip(inpshape, self.issequence, self.dim)
        ]

        # Check if inpdim is consistent
        assert all([indim == self.inpdim[0] for indim in self.inpdim
                    ]), "Can't concatenate 2D with 3D inputs."

        # Make merge layers
        self.mergelayers = [
            mergelayer(numinp=len(conn),
                       dim=[self.dim[node] for node in conn],
                       issequence=[self.issequence[node] for node in conn],
                       inpshape=[inpshape[node] for node in conn])
            if pyk.smartlen(conn) != 1 else None for conn in self.connections
        ]

        # Shape inference
        self.inpshape = list(inpshape) if inpshape is not None else [[
            None,
        ] * indim for indim in self.inpdim]

        # Containers for input and output
        self.x = pyk.delist([
            T.tensor('floatX', [
                False,
            ] * indim,
                     name='x{}:'.format(inpnum) + str(id(self)))
            for inpnum, indim in enumerate(self.inpdim)
        ])
        self.y = pyk.delist([
            T.tensor('floatX', [
                False,
            ] * self.inpdim[pyk.obj2list(self.connections[outnum])[0]],
                     name='y:' + str(id(self)))
            for outnum in range(self.numout)
        ])
Пример #7
0
def parselayerinfo(dim=None,
                   inpdim=None,
                   issequence=None,
                   allowsequences=None,
                   numinp=None,
                   inpshape=None,
                   verbose=True):
    parsey = {
        'dim': dim,
        'inpdim': inpdim,
        'issequence': issequence,
        'allowsequences': allowsequences,
        'numinp': numinp,
        'inpshape': inpshape
    }

    # Parse from inpshape
    if parsey['inpshape'] is not None:
        # Make sure inpshape is a list
        assert isinstance(
            parsey['inpshape'],
            list), "inpshape must be a list, e.g. [None, 3, None, None]."

        # Fetch number of inputs
        if pyk.islistoflists(parsey['inpshape']):
            _numinp = len(parsey['inpshape'])
            _inpdim = [len(ishp) for ishp in parsey['inpshape']]
        else:
            _numinp = 1
            _inpdim = len(parsey['inpshape'])

        # Write to parsed (if not written already)
        # numinp
        parsey['numinp'] = _numinp if parsey['numinp'] is None else parsey[
            'numinp']
        # Consistency check
        assert parsey['numinp'] == _numinp, "The provided inpshape requires numinp = {}, " \
                                            "but the value given was {}".format(_numinp, parsey['numinp'])
        # inpdim
        parsey['inpdim'] = _inpdim if parsey['inpdim'] is None else parsey[
            'inpdim']
        assert parsey['inpdim'] == _inpdim, "The provided inpshape requires inpdim = {}, " \
                                            "but the value given was {}".format(_inpdim, parsey['inpdim'])

    # Check if dim, inpdim, issequence or allowsequences is a list of multiple elements and numinp is not given.
    if parsey['numinp'] is None:
        for argname in ['dim', 'inpdim', 'issequence', 'allowsequences']:
            if isinstance(parsey[argname], list):
                parsey['numinp'] = len(parsey[argname])

    # Parse from numinp
    if parsey['numinp'] is not None:
        for argname, argtype in zip(
            ['dim', 'inpdim', 'issequence', 'allowsequences'],
            [int, int, bool, bool]):
            if isinstance(parsey[argname], argtype) and parsey['numinp'] > 1:
                # If numinp is > 1 and allowseqences or issequence or inpdim or dim is bool or bool or int or int,
                # assume that the user (or the author) is too lazy to type in a list.
                parsey[argname] = [
                    parsey[argname],
                ] * parsey['numinp']
            elif isinstance(parsey[argname], list) and parsey['numinp'] > 1:
                # If the user was not lazy, make sure the given list sizes check out
                assert len(parsey[argname]) == parsey['numinp'], \
                    "{} must be a {} or a list of length {} (= numinp).".format(argname, argtype, parsey['numinp'])

        # Check if inpshape is consistent
        if parsey['inpshape'] is not None and parsey['numinp'] > 1:
            assert pyk.islistoflists(parsey['inpshape']) and len(
                parsey['inpshape']) == parsey['numinp']

    else:
        if verbose:
            warn("Guessing that numinp = 1.")
        # Guess numinp = 1.
        parsey['numinp'] = 1

    # Parse allowsequences
    # At this point, allowsequences must be known (or no conclusions can be drawn on issequence and dim)
    if parsey['allowsequences'] is None:
        if verbose:
            warn("Guessing that sequences are allowed.")
        parsey['allowsequences'] = pyk.delist([
            True,
        ] * parsey['numinp'])
    else:
        # Okay, so it's known if sequences are allowed. Check if issequence is consistent.
        if pyk.obj2list(parsey['allowsequences']) == [
                False,
        ] * parsey['numinp'] and parsey['issequence'] is not None:
            # If sequences are not allowed, make sure issequence is False
            assert pyk.obj2list(parsey['issequence']) == [False,] * parsey['numinp'], \
                "Input(s) are not allowed to be sequential, yet they are."

    # Parse issequence
    if parsey['issequence'] is not None:
        # Delist issequence
        parsey['issequence'] = pyk.delist(parsey['issequence']) \
            if isinstance(parsey['issequence'], list) else parsey['issequence']

        # Check if issequence is consistent with everything
        if isinstance(parsey['issequence'], list):
            assert len(parsey['issequence']) == parsey['numinp'], "issequence must be a list of the same lenght as " \
                                                                  "numinp = {} if numinp > 1.".format(parsey['numinp'])

        # Check if consistent with allowsequences. At this point, issequence may have None's.
        assert all([(bool(isseq) and allowseq) or not isseq
                    for isseq, allowseq in zip(pyk.obj2list(parsey['issequence']),
                                               pyk.obj2list(parsey['allowsequences']))]), \
            "Input is a sequence although it's not allowed to. " \
            "issequence = {}, allowsequences = {}.".format(parsey['issequence'], parsey['allowsequences'])

    else:
        if verbose:
            warn("Guessing that input(s) is(are) not sequential.")
        parsey['issequence'] = pyk.delist([
            False,
        ] * parsey['numinp'])

    # Parse inpdim
    # Compute expected inpdim from what's known
    # Check in from issequence
    _inpdim = pyk.delist(
        [5 if isseq else None for isseq in pyk.obj2list(parsey['issequence'])])
    # Check in from dim
    if parsey['dim'] is not None:
        _inpdim = pyk.delist([
            5 if d == 3 else indim for d, indim in zip(
                pyk.obj2list(parsey['dim']), pyk.obj2list(_inpdim))
        ])
        _inpdim = pyk.delist([
            4 if (d == 2 and not isseq) else indim for d, indim, isseq in zip(
                pyk.obj2list(parsey['dim']), pyk.obj2list(_inpdim),
                pyk.obj2list(parsey['issequence']))
        ])

    if parsey['inpdim'] is None:
        # Make sure there are no None's remaining in _inpdim
        assert None not in pyk.obj2list(
            _inpdim
        ), "Input dimensionality could not be parsed due to missing information."
        parsey['inpdim'] = _inpdim
    else:
        assert pyk.smartlen(parsey['inpdim']) == pyk.smartlen(_inpdim), \
            "Expected {} elements in inpdim, got {}.".format(pyk.smartlen(_inpdim), pyk.smartlen(parsey['inpdim']))
        # Check consistency with the expected _inpdim
        assert all([_indim == indim for _indim, indim in zip(pyk.obj2list(_inpdim), pyk.obj2list(parsey['inpdim']))
                    if _indim is not None]), \
            "Provided inpdim is inconsistent with either dim or issequence."

    # Parse dim
    # Compute expected _inpdim from what's known
    _dim = pyk.delist([
        2 if (indim == 4 or isseq) else 3 for indim, isseq in zip(
            pyk.obj2list(parsey['inpdim']), pyk.obj2list(parsey['issequence']))
    ])
    # Check in from dim
    if parsey['dim'] is None:
        parsey['dim'] = _dim
    else:
        assert parsey[
            'dim'] == _dim, "Given dim ({}) is not consistent with expectation ({})".format(
                parsey['dim'], _dim)

    # Reparse inpshape
    if parsey['inpshape'] is None:
        parsey['inpshape'] = pyk.delist([[
            None,
        ] * indim for indim in pyk.obj2list(parsey['inpdim'])])

    # Return parsey :(
    return parsey
Пример #8
0
    def __init__(self, connections, merge=True, dim=2, issequence=False, inpshape=None):
        """
        :type connections: list
        :param connections: List of connections. E.g.: [[0, 1], 1, [0, 1, 2]] would merge inputs 0 & 1 and write to
                            first output slot, 1 to second output slot and 0, 1 & 2 merged to the third output slot.
        :param dim:
        :param issequence:
        :param inpshape:
        :return:
        """
        super(circuitlayer, self).__init__()

        # Compute the number of i/o slots
        self.numinp = len(pyk.unique(list(pyk.flatten(connections))))
        self.numout = len(connections)
        self.merge = bool(merge)
        # Parse
        dim = [dim, ] * self.numinp if isinstance(dim, int) and self.numinp is not None else dim
        issequence = [issequence, ] * self.numinp if isinstance(issequence, bool) and self.numinp is not None \
            else issequence

        # Parse inpshape if numinp, dim, issequence is given
        if self.numinp is not None and dim is not None and inpshape is None:
            if 3 in dim:
                # issequence doesn't matter, inpshape can still be filled
                inpshape = [[None, ] * 5 for _ in dim]
            else:
                # dim is 2, but input might still be sequential
                if issequence is None:
                    pass
                else:
                    if issequence:
                        # issequence is false and dim = 2, ergo 2D data
                        inpshape = [[None, ] * 4 for _ in dim]
                    else:
                        # issequence is true, ergo 2D sequential data
                        inpshape = [[None, ] * 5 for _ in dim]

        # Meta
        self.connections = connections
        self.dim = dim if dim is not None else [{4: 2, 5: 2}[len(ishp)] for ishp in inpshape]
        self.allowsequences = True
        self.issequence = [idim == 2 and len(ishp) == 5 for idim, ishp in zip(self.dim, inpshape)] \
            if issequence is None else issequence
        self.inpdim = [len(ishp) if ishp is not None else 5 if iisseq else {2: 4, 3: 5}[idim]
                       for ishp, iisseq, idim in zip(inpshape, self.issequence, self.dim)]

        # Check if inpdim is consistent
        assert all([indim == self.inpdim[0] for indim in self.inpdim]), "Can't concatenate 2D with 3D inputs."

        # Make merge layers
        self.mergelayers = [mergelayer(numinp=len(conn),
                                       dim=[self.dim[node] for node in conn],
                                       issequence=[self.issequence[node] for node in conn],
                                       inpshape=[inpshape[node] for node in conn])
                            if pyk.smartlen(conn) != 1 else None for conn in self.connections]

        # Shape inference
        self.inpshape = list(inpshape) if inpshape is not None else [[None, ] * indim for indim in self.inpdim]

        # Containers for input and output
        self.x = pyk.delist([T.tensor('floatX', [False, ] * indim, name='x{}:'.format(inpnum) + str(id(self)))
                             for inpnum, indim in enumerate(self.inpdim)])
        self.y = pyk.delist([T.tensor('floatX', [False, ] * self.inpdim[pyk.obj2list(self.connections[outnum])[0]],
                                      name='y:' + str(id(self)))
                            for outnum in range(self.numout)])