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))
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)
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
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
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
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) ])
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
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)])