def scalar_value(self): if is_number(self.value): return self.value elif self.neutral or self.value == "": return 0.0 else: return 1.0
def as_ast_node(obj): if isinstance(obj, Node): return obj elif is_number(obj): return FixedScalar(obj) elif isinstance(obj, nengo.Network) and hasattr(obj, 'output'): output = obj.output else: output = obj try: # Trying to create weakref on access of weak dict can raise TypeError vocab = output_vocab_registry[output] except (KeyError, TypeError) as cause: if getattr(output, 'size_out', 0) == 1: return ModuleOutput(output, TScalar) err = SpaTypeError( "{} was not registered as a SPA output.".format(output)) err.__suppress_context__ = True raise err finally: err = None # prevent cyclic reference, traceback might reference this if vocab is None: return ModuleOutput(output, TScalar) else: return ModuleOutput(output, TVocabulary(vocab))
def __init__(self, block_info, inputs, dt): self.dt = dt self.probes = OrderedDict() for block in block_info.blocks: for probe in block.probes: self.probes[probe] = block_info.slices[block] self.filters = {} self.filter_pos = {} for probe, sl in self.probes.items(): if probe.synapse is not None: if probe.weights is None or is_number(probe.weights): size = sl.stop - sl.start else: assert is_array(probe.weights) and probe.weights.ndim == 2 size = probe.weights.shape[1] self.filters[probe] = probe.synapse.make_step( shape_in=(size, ), shape_out=(size, ), dt=self.dt, rng=None, dtype=np.float32, ) self.filter_pos[probe] = 0 self.outputs = defaultdict(list)
def parse(self, text): """Evaluate a text string and return the corresponding SemanticPointer. This uses the Python ``eval()`` function, so any Python operators that have been defined for SemanticPointers are valid (``+``, ``-``, ``*``, ``~``, ``()``). Any terms do not exist in the vocabulary will be automatically generated. Valid semantic pointer terms must start with a capital letter. If the expression returns a scalar (int or float), a scaled version of the identity SemanticPointer will be returned. """ # The following line does everything. Note that self is being # passed in as the locals dictionary, and thanks to the __getitem__ # implementation, this will automatically create new semantic # pointers as needed. try: value = eval(text, {}, self) except NameError: raise SpaParseError( "Semantic pointers must start with a capital letter.") if is_number(value): value = value * self.identity if not isinstance(value, pointer.SemanticPointer): raise SpaParseError( "The result of parsing '%s' is not a SemanticPointer" % text) return value
def coerce(self, instance, data): data = super(PiecewiseDataParam, self).coerce(instance, data) size_out = None for time, value in iteritems(data): if not is_number(time): raise ValidationError("Keys must be times (floats or ints), " "not %r" % type(time).__name__, attr='data', obj=instance) # figure out the length of this item if callable(value): try: value = np.ravel(value(time)) except Exception: raise ValidationError( "callable object for time step %.3f " "should return a numerical constant" % time, attr='data', obj=instance) else: value = np.ravel(value) data[time] = value size = value.size # make sure this is the same size as previous items if size != size_out and size_out is not None: raise ValidationError("time %g has size %d instead of %d" % (time, size, size_out), attr='data', obj=instance) size_out = size return data
def validate(self, instance, num): if num is not None: if not is_number(num): raise ValueError("Must be a number; got '%s'" % num) if self.low is not None and num < self.low: raise ValueError("Number must be greater than %s" % self.low) if self.high is not None and num > self.high: raise ValueError("Number must be less than %s" % self.high) super(NumberParam, self).validate(instance, num)
def build_nrn_connection(model, conn): # Create random number generator rng = np.random.RandomState(model.seeds[conn]) # Check pre-conditions assert isinstance(conn.pre, nengo.Ensemble) assert not isinstance(conn.pre.neuron_type, nengo.neurons.Direct) # FIXME assert no rate neurons are used. How to do that? # Get input signal # FIXME this should probably be # model.sig[conn]['in'] = model.sig[conn.pre]["out"] # in both cases if isinstance(conn.pre, nengo.ensemble.Neurons): model.sig[conn]["in"] = model.sig[conn.pre.ensemble]["out"] else: model.sig[conn]["in"] = model.sig[conn.pre]["out"] # Figure out type of connection if isinstance(conn.post, nengo.ensemble.Neurons): raise NotImplementedError() # TODO elif isinstance(conn.post.neuron_type, Compartmental): pass else: raise AssertionError("This function should only be called if post neurons are " "compartmental.") # Solve for weights # FIXME just assuming solver is a weight solver, may that break? # Default solver should probably also produce sparse solutions for # performance reasons eval_points, activities, targets = build_linear_system(model, conn, rng=rng) # account for transform transform = full_transform(conn) targets = np.dot(targets, transform.T) weights, solver_info = conn.solver(activities, targets, rng=rng, E=model.params[conn.post].scaled_encoders.T) # Synapse type synapse = conn.synapse if is_number(synapse): synapse = ExpSyn(synapse) # Connect # TODO: Why is this adjustment of the weights necessary? weights = weights / synapse.tau / 5.0 * 0.1 connections = [[] for i in range(len(weights))] for i, cell in enumerate(ens_to_cells[conn.post]): for j, w in enumerate(weights[:, i]): if w >= 0.0: x = np.random.rand() connections[j].append(synapse.create(cell.neuron.apical(x), w * (x + 1))) else: connections[j].append(synapse.create(cell.neuron.soma(0.5), w)) # 3. Add operator creating events for synapses if pre neuron fired model.add_op(NrnTransmitSpikes(model.sig[conn]["in"], connections))
def filtered_signal(model, owner, sig, synapse): # Note: we add a filter here even if synapse < dt, # in order to avoid cycles in the op graph. If the filter # is explicitly set to None (e.g. for a passthrough node) # then cycles can still occur. if is_number(synapse): synapse = Lowpass(synapse) assert isinstance(synapse, Synapse) model.build(synapse, owner, sig) return model.sig[owner]['synapse_out']
def __rmul__(self, other): """Multiplication of two SemanticPointers is circular convolution. If mutliplied by a scaler, we do normal multiplication. """ if isinstance(other, SemanticPointer): return self.convolve(other) elif is_number(other): return SemanticPointer(data=self.v * other) else: raise Exception('Can only multiply by SemanticPointers or scalars')
def _parse_var(self, var): if is_number(var): return NumExp(var) elif isinstance(var, str): return '"%s"' % var elif isinstance(var, (list, np.ndarray)): if isinstance(var, np.ndarray): var = var.tolist() self._check_vector_length(len(var)) return [self._parse_var(v) for v in var] else: return var
def __imul__(self, other): """Multiplication of two SemanticPointers is circular convolution. If mutliplied by a scaler, we do normal multiplication. """ if isinstance(other, SemanticPointer): self.v = np.fft.ifft(np.fft.fft(self.v) * np.fft.fft(other.v)).real elif is_number(other): self.v *= other else: raise Exception('Can only multiply by SemanticPointers or scalars') return self
def __mul__(self, other): """Multiplication of two SemanticPointers is circular convolution. If multiplied by a scalar, we do normal multiplication. """ if isinstance(other, SemanticPointer): return self.convolve(other) elif is_number(other): return SemanticPointer(data=self.v * other) else: raise NotImplementedError( "Can only multiply by SemanticPointers or scalars")
def validate(self, instance, num): if num is not None: if not is_number(num): raise ValueError("Must be a number; got '%s'" % num) low_comp = 0 if self.low_open else -1 if self.low is not None and compare(num, self.low) <= low_comp: raise ValueError("Value must be greater than %s%s (got %s)" % ( "" if self.low_open else "or equal to ", self.low, num)) high_comp = 0 if self.high_open else 1 if self.high is not None and compare(num, self.high) >= high_comp: raise ValueError("Value must be less than %s%s (got %s)" % ( "" if self.high_open else "or equal to ", self.high, num)) super(NumberParam, self).validate(instance, num)
def __mul__(self, other): if isinstance(other, Symbol): if other.symbol == '1': return self if self.symbol == '1': return other return Symbol('(%s * %s)' % (self.symbol, other.symbol)) if is_number(other): if other == 1: return self if self.symbol == '1': return Symbol('%g' % other) return Symbol('(%s * %g)' % (self.symbol, other)) return NotImplemented
def on_add(self, spa): """Form the connections into the BG to compute the utilty values. Each action's condition variable contains the set of computations needed for that action's utility value, which is the input to the basal ganglia. """ Module.on_add(self, spa) self.spa = spa self.actions.process(spa) # parse the actions for i, action in enumerate(self.actions.actions): cond = action.condition.expression # the basal ganglia hangles the condition part of the action; # the effect is handled by the thalamus # Note: A Source is an output from a module, and a Symbol is # text that can be parsed to be a SemanticPointer for c in cond.items: if isinstance(c, DotProduct): if ((isinstance(c.item1, Source) and c.item1.inverted) or (isinstance(c.item2, Source) and c.item2.inverted)): raise NotImplementedError( "Inversion in subexpression '%s' from action '%s' " "is not supported by the Basal Ganglia." % (c, action)) if isinstance(c.item1, Source): if isinstance(c.item2, Source): # dot product between two different sources self.add_compare_input(i, c.item1, c.item2, c.scale) else: self.add_dot_input(i, c.item1, c.item2, c.scale) else: # enforced in DotProduct constructor assert isinstance(c.item2, Source) self.add_dot_input(i, c.item2, c.item1, c.scale) elif isinstance(c, Source): self.add_scalar_input(i, c) elif is_number(c): self.add_bias_input(i, c) else: raise NotImplementedError( "Subexpression '%s' from action '%s' is not supported " "by the Basal Ganglia." % (c, action))
def _sys2form(sys): if isinstance(sys, LinearSystem): return _LSYS elif isinstance(sys, LinearFilter): return _LFILT elif is_number(sys): return _NUM elif len(sys) == 2: return _TF elif len(sys) == 3: return _ZPK elif len(sys) == 4: return _SS else: raise ValueError( "sys must be an instance of LinearSystem, a scalar, or a tuple of " "2 (tf), 3 (zpk), or 4 (ss) arrays.")
def _sys2form(sys): if isinstance(sys, LinearSystem): return _LSYS elif isinstance(sys, LinearFilter): return _LFILT elif is_number(sys): return _NUM elif len(sys) == 2: return _TF elif len(sys) == 3: return _ZPK elif len(sys) == 4: return _SS else: raise ValueError( "sys must be an instance of LinearSystem, LinearFilter, a scalar, " "or a tuple of 2 (tf), 3 (zpk), or 4 (ss) arrays.")
def filt(signal, synapse, dt, axis=0, x0=None, copy=True): """Filter ``signal`` with ``synapse``. Parameters ---------- signal : array_like The signal to filter. syanpse : float, Synapse The synapse model with which to filter the signal. If a float is passed in, it will be interpreted as the ``tau`` parameter of a lowpass filter. axis : integer, optional The axis along which to filter. Default: 0. x0 : array_like, optional The starting state of the filter output. copy : boolean, optional Whether to copy the input data, or simply work in-place. Default: True. """ if is_number(synapse): synapse = Lowpass(synapse) filtered = np.array(signal, copy=copy) filt_view = np.rollaxis(filtered, axis=axis) # rolled view on filtered # --- buffer method if x0 is not None: if x0.shape != filt_view[0].shape: raise ValidationError("'x0' with shape %s must have shape %s" % (x0.shape, filt_view[0].shape), attr='x0') signal_out = np.array(x0) else: # signal_out is our buffer for the current filter state signal_out = np.zeros_like(filt_view[0]) step = synapse.make_step(dt, signal_out) for i, signal_in in enumerate(filt_view): step(signal_in) filt_view[i] = signal_out return filtered
def filt(signal, synapse, dt, axis=0, x0=None, copy=True): """Filter ``signal`` with ``synapse``. Parameters ---------- signal : array_like The signal to filter. syanpse : float, Synapse The synapse model with which to filter the signal. If a float is passed in, it will be interpreted as the ``tau`` parameter of a lowpass filter. axis : integer, optional The axis along which to filter. Default: 0. x0 : array_like, optional The starting state of the filter output. copy : boolean, optional Whether to copy the input data, or simply work in-place. Default: True. """ if is_number(synapse): synapse = Lowpass(synapse) filtered = np.array(signal, copy=copy) filt_view = np.rollaxis(filtered, axis=axis) # rolled view on filtered # --- buffer method if x0 is not None: if x0.shape != filt_view[0].shape: raise ValueError("'x0' with shape %s must have shape %s" % (x0.shape, filt_view[0].shape)) signal_out = np.array(x0) else: # signal_out is our buffer for the current filter state signal_out = np.zeros_like(filt_view[0]) step = synapse.make_step(dt, signal_out) for i, signal_in in enumerate(filt_view): step(signal_in) filt_view[i] = signal_out return filtered
def filtfilt(signal, synapse, dt, axis=0, copy=True): """Zero-phase filtering of ``signal`` using the ``syanpse`` filter. This is done by filtering the input in forward and reverse directions. Equivalent to scipy and Matlab's filtfilt function using the filter defined by the synapse object passed in. Parameters ---------- signal : array_like The signal to filter. synapse : float, Synapse The synapse model with which to filter the signal. If a float is passed in, it will be interpreted as the ``tau`` parameter of a lowpass filter. axis : integer, optional The axis along which to filter. Default: 0. copy : boolean, optional Whether to copy the input data, or simply work in-place. Default: True. """ if is_number(synapse): synapse = Lowpass(synapse) filtered = np.array(signal, copy=copy) filt_view = np.rollaxis(filtered, axis=axis) signal_out = np.zeros_like(filt_view[0]) step = synapse.make_step(dt, signal_out) for i, signal_in in enumerate(filt_view): step(signal_in) filt_view[i] = signal_out # Flip the filt_view and filter again filt_view = filt_view[::-1] for i, signal_in in enumerate(filt_view): step(signal_in) filt_view[i] = signal_out return filtered
def _mul(self, other, swap=False): if is_array(other): raise TypeError( "Multiplication of Semantic Pointers with arrays in not " "allowed.") elif is_number(other): return SemanticPointer(data=self.v * other, vocab=self.vocab, algebra=self.algebra, name=self._get_binary_name( other, "*", swap)) elif isinstance(other, Fixed): if other.type == TScalar: return SemanticPointer(data=self.v * other.evaluate(), vocab=self.vocab, algebra=self.algebra, name=self._get_binary_name( other, "*", swap)) else: return self._bind(other, swap=swap) else: return NotImplemented
def coerce(self, instance, num): if num is not None: if is_array(num) and num.shape == (): num = num.item() # convert scalar array to Python object if not is_number(num): raise ValidationError("Must be a number; got '%s'" % num, attr=self.name, obj=instance) low_comp = 0 if self.low_open else -1 if self.low is not None and compare(num, self.low) <= low_comp: raise ValidationError( "Value must be greater than %s%s (got %s)" % ( "" if self.low_open else "or equal to ", self.low, num), attr=self.name, obj=instance) high_comp = 0 if self.high_open else 1 if self.high is not None and compare(num, self.high) >= high_comp: raise ValidationError( "Value must be less than %s%s (got %s)" % ( "" if self.high_open else "or equal to ", self.high, num), attr=self.name, obj=instance) return super(NumberParam, self).coerce(instance, num)
def fractional_bind(self, A, b): """Fractional circular convolution.""" if not is_number(b): raise ValueError("b must be a scalar.") return np.fft.ifft(np.fft.fft(A, axis=0)**b, axis=0) #.real
def piecewise(data): """Create a piecewise constant function from a dictionary. Given an input of data={0: 0, 0.5: 1, 0.75: -1, 1: 0} this will generate a function that returns 0 up until t=0.5, then outputs a 1 until t=0.75, then a -1 until t=1, and then returns 0 after that. This is meant as a shortcut for:: def function(t): if t < 0.5: return 0 elif t < 0.75 return 1 elif t < 1: return -1 else: return 0 The keys in the dictionary must be times (floats or ints). The values in the data dictionary can be floats, lists, or functions that return floats or lists. All lists must be of the same length. For times before the first specified time, it will default to zero (of the correct length). This means the above example can be simplified to:: piecewise({0.5: 1, 0.75: -1, 1: 0}) Parameters ---------- data : dict The values to change to. Keys are the beginning time for the value. Values can be int, float, list, or functions that return those. Returns ------- function: A function that takes a variable t and returns the corresponding value from the dictionary. Examples -------- >>> func = piecewise({0.5: 1, 0.75: -1, 1: 0}) >>> func(0.2) [0] >>> func(0.58) [1] >>> func = piecewise({0.5: [1, 0], 0.75: [0, 1]}) >>> func(0.2) [0,0] >>> func(0.58) [1,0] >>> func(100) [0,1] >>> import math >>> func = piecewise({0: math.sin, 0.5: math.cos}) >>> func(0.499) [0.47854771647582706] >>> func(0.5) [0.8775825618903728] """ # first, sort the data (to simplify finding the right element # when calling the function) output_length = None # the dimensionality of the returned values for time in data: if not is_number(time): raise TypeError('Keys must be times (floats or ints), not "%s"' % repr(time.__class__)) # figure out the length of this item if callable(data[time]): length = np.asarray(data[time](0.0)).size else: data[time] = np.asarray(data[time]) length = data[time].size # make sure this is the same length as previous items if length != output_length and output_length is not None: raise ValueError('Invalid data for piecewise function: ' 'time %4g has %d items instead of %d' % (time, length, output_length)) output_length = length # make a default output of 0 when t before what was passed data[np.finfo(float).min] = np.zeros(output_length) ordered_data = OrderedDict(sorted(iteritems(data))) # build the function to return def piecewise_function(t, data=ordered_data): # get the t we'll use for output for time in (time for time in data if time <= t): out_t = time # if it's a function, call it if callable(data[out_t]): return np.asarray(data[out_t](t)) return data[out_t] return piecewise_function
def __set__(self, conn, synapse): if is_number(synapse): synapse = Lowpass(synapse) self.validate(conn, synapse) self.data[conn] = synapse
def __add__(self, other): if is_number(other): other = Symbol('%g' % other) if isinstance(other, Symbol): return Symbol('(%s + %s)' % (self.symbol, other.symbol)) return NotImplemented
def arg_string(args): return "{%s}" % ", ".join( "%s: %s" % (k, arg_string(v)) if isinstance(v, dict) else "%s: %0.1e" % (k, v) if is_number(v) else "%s: %s" % (k, v) for k, v in args.items())
def __set__(self, instance, synapse): if is_number(synapse): synapse = Lowpass(synapse) super(SynapseParam, self).__set__(instance, synapse)
def __mul__(self, other): if isinstance(other, Source): return Convolution(self, other) elif is_number(other) or isinstance(other, Symbol): return Source(self.name, self.transform*other, self.inverted) return NotImplemented
def build_nrn_connection(model, conn): # Create random number generator rng = np.random.RandomState(model.seeds[conn]) # Check pre-conditions assert isinstance(conn.pre, nengo.Ensemble) assert not isinstance(conn.pre.neuron_type, nengo.neurons.Direct) # FIXME assert no rate neurons are used. How to do that? # Get input signal # FIXME this should probably be # model.sig[conn]['in'] = model.sig[conn.pre]["out"] # in both cases if isinstance(conn.pre, nengo.ensemble.Neurons): model.sig[conn]['in'] = model.sig[conn.pre.ensemble]['out'] else: model.sig[conn]['in'] = model.sig[conn.pre]["out"] # Figure out type of connection if isinstance(conn.post, nengo.ensemble.Neurons): raise NotImplementedError() # TODO elif isinstance(conn.post.neuron_type, Compartmental): pass else: raise AssertionError( "This function should only be called if post neurons are " "compartmental.") # Solve for weights # FIXME just assuming solver is a weight solver, may that break? # Default solver should probably also produce sparse solutions for # performance reasons eval_points, activities, targets = build_linear_system(model, conn, rng=rng) # account for transform transform = full_transform(conn) targets = np.dot(targets, transform.T) weights, solver_info = conn.solver( activities, targets, rng=rng, E=model.params[conn.post].scaled_encoders.T) # Synapse type synapse = conn.synapse if is_number(synapse): synapse = ExpSyn(synapse) # Connect # TODO: Why is this adjustment of the weights necessary? weights = weights / synapse.tau / 5. * .1 connections = [[] for i in range(len(weights))] for i, cell in enumerate(ens_to_cells[conn.post]): for j, w in enumerate(weights[:, i]): if w >= 0.0: x = np.random.rand() connections[j].append( synapse.create(cell.neuron.apical(x), w * (x + 1))) else: connections[j].append(synapse.create(cell.neuron.soma(0.5), w)) # 3. Add operator creating events for synapses if pre neuron fired model.add_op(NrnTransmitSpikes(model.sig[conn]['in'], connections))
def label(self): if is_number(self.value): return 'numerical' elif self.neutral or self.value == "": return 'neutral' return self.value
def as_node(obj): if is_number(obj): obj = FixedScalar(obj) return obj
def coerce(self, instance, synapse): synapse = Lowpass(synapse) if is_number(synapse) else synapse self.check_type(instance, synapse, Synapse) return super(SynapseParam, self).coerce(instance, synapse)
def __mul__(self, other): if isinstance(other, Source): return Convolution(self, other) elif is_number(other) or isinstance(other, Symbol): return Source(self.name, self.transform * other, self.inverted) return NotImplemented