def __init__(self, steps=None, name=None): super(DynamicalSystem, self).__init__(name=name) # step functions if steps is None: steps = ('update', ) self.steps = Collector() if isinstance(steps, tuple): for step in steps: if isinstance(step, str): self.steps[step] = getattr(self, step) elif callable(step): self.steps[step.__name__] = step else: raise ModelBuildError( _error_msg.format(steps[0].__class__, str(steps[0]))) elif isinstance(steps, dict): for key, step in steps.items(): if callable(step): self.steps[key] = step else: raise ModelBuildError( _error_msg.format(steps.__class__, str(steps))) else: raise ModelBuildError( _error_msg.format(steps.__class__, str(steps)))
def __init__(self, *ds_tuple, steps=None, name=None, **ds_dict): # integrative step function if steps is None: steps = ('update', ) super(Container, self).__init__(steps=steps, name=name) # children dynamical systems self.implicit_nodes = Collector() for ds in ds_tuple: if not isinstance(ds, DynamicalSystem): raise ModelBuildError( f'{self.__class__.__name__} receives instances of ' f'DynamicalSystem, however, we got {type(ds)}.') if ds.name in self.implicit_nodes: raise ValueError( f'{ds.name} has been paired with {ds}. Please change a unique name.' ) self.register_implicit_nodes({node.name: node for node in ds_tuple}) for key, ds in ds_dict.items(): if not isinstance(ds, DynamicalSystem): raise ModelBuildError( f'{self.__class__.__name__} receives instances of ' f'DynamicalSystem, however, we got {type(ds)}.') if key in self.implicit_nodes: raise ValueError( f'{key} has been paired with {ds}. Please change a unique name.' ) self.register_implicit_nodes(ds_dict)
def check_post_attrs(self, *attrs): """Check whether post group satisfies the requirement.""" if not hasattr(self, 'post'): raise ModelBuildError('Please call __init__ function first.') for attr in attrs: if not isinstance(attr, str): raise ValueError(f'Must be string. But got {attr}.') if not hasattr(self.post, attr): raise ModelBuildError( f'{self} need "pre" neuron group has attribute "{attr}".')
def __init__(self, size, delay, dtype=None, dt=None, **kwargs): # dt self.dt = bm.get_dt() if dt is None else dt # data size if isinstance(size, int): size = (size, ) if not isinstance(size, (tuple, list)): raise ModelBuildError( f'"size" must a tuple/list of int, but we got {type(size)}: {size}' ) self.size = tuple(size) # delay time length self.delay = delay # data and operations if isinstance(delay, (int, float)): # uniform delay self.uniform_delay = True self.num_step = int(pm.ceil(delay / self.dt)) + 1 self.out_idx = bm.Variable(bm.array([0], dtype=bm.uint32)) self.in_idx = bm.Variable( bm.array([self.num_step - 1], dtype=bm.uint32)) self.data = bm.Variable( bm.zeros((self.num_step, ) + self.size, dtype=dtype)) else: # non-uniform delay self.uniform_delay = False if not len(self.size) == 1: raise NotImplementedError( f'Currently, BrainPy only supports 1D heterogeneous ' f'delays, while we got the heterogeneous delay with ' f'{len(self.size)}-dimensions.') self.num = size2len(size) if bm.ndim(delay) != 1: raise ModelBuildError(f'Only support a 1D non-uniform delay. ' f'But we got {delay.ndim}D: {delay}') if delay.shape[0] != self.size[0]: raise ModelBuildError( f"The first shape of the delay time size must " f"be the same with the delay data size. But " f"we got {delay.shape[0]} != {self.size[0]}") delay = bm.around(delay / self.dt) self.diag = bm.array(bm.arange(self.num), dtype=bm.int_) self.num_step = bm.array(delay, dtype=bm.uint32) + 1 self.in_idx = bm.Variable(self.num_step - 1) self.out_idx = bm.Variable(bm.zeros(self.num, dtype=bm.uint32)) self.data = bm.Variable( bm.zeros((self.num_step.max(), ) + size, dtype=dtype)) super(ConstantDelay, self).__init__(**kwargs)
def __init__(self, size, times, indices, need_sort=True, name=None): super(SpikeTimeInput, self).__init__(size=size, name=name) # parameters if len(indices) != len(times): raise ModelBuildError(f'The length of "indices" and "times" must be the same. ' f'However, we got {len(indices)} != {len(times)}.') self.num_times = len(times) # data about times and indices self.i = bm.Variable(bm.zeros(1, dtype=bm.int_)) self.times = bm.Variable(bm.asarray(times, dtype=bm.float_)) self.indices = bm.Variable(bm.asarray(indices, dtype=bm.int_)) self.spike = bm.Variable(bm.zeros(self.num, dtype=bool)) if need_sort: sort_idx = bm.argsort(times) self.indices.value = self.indices[sort_idx] self.times.value = self.times[sort_idx] # functions def cond_fun(t): return bm.logical_and(self.i[0] < self.num_times, t >= self.times[self.i[0]]) def body_fun(t): self.spike[self.indices[self.i[0]]] = True self.i[0] += 1 self._run = bm.make_while(cond_fun, body_fun, dyn_vars=self.vars())
def register_constant_delay(self, key, size, delay, dtype=None): """Register a constant delay, whose update method will be appended into the ``self.steps`` in this host class. >>> import brainpy as bp >>> group = bp.NeuGroup(10) >>> group.steps {'update': <bound method NeuGroup.update of <brainpy.simulation.brainobjects.neuron.NeuGroup object at 0xxxx>>} >>> delay1 = group.register_constant_delay('delay1', size=(10,), delay=2) >>> delay1 <brainpy.simulation.brainobjects.delays.ConstantDelay at 0x219d5188280> >>> group.steps {'update': <bound method NeuGroup.update of <brainpy.simulation.brainobjects.neuron.NeuGroup object at 0xxxx>>, 'delay1_update': <bound method ConstantDelay.update of <brainpy.simulation.brainobjects.delays.ConstantDelay object at 0xxxx>>} >>> delay1.data.shape (20, 10) Parameters ---------- key : str The delay name. size : int, list of int, tuple of int The delay data size. delay : int, float, ndarray The delay time, with the unit same with `brainpy.math.get_dt()`. dtype : optional The data type. Returns ------- delay : ConstantDelay An instance of ConstantDelay. """ global ConstantDelay if ConstantDelay is None: from brainpy.building.brainobjects.delays import ConstantDelay if not hasattr(self, 'steps'): raise ModelBuildError( 'Please initialize the super class first before ' 'registering constant_delay. \n\n' 'super(YourClassName, self).__init__(**kwargs)') if not key.isidentifier(): raise ValueError(f'{key} is not a valid identifier.') cdelay = ConstantDelay(size=size, delay=delay, name=f'{self.name}_delay_{key}', dtype=dtype) self.steps[f'{key}_update'] = cdelay.update return cdelay
def __init__(self, pre, post, conn=None, name=None, steps=('update', )): # pre or post neuron group # ------------------------ if not isinstance(pre, NeuGroup): raise ModelBuildError('"pre" must be an instance of NeuGroup.') if not isinstance(post, NeuGroup): raise ModelBuildError('"post" must be an instance of NeuGroup.') self.pre = pre self.post = post # connectivity # ------------ if isinstance(conn, TwoEndConnector): self.conn = conn(pre.size, post.size) elif isinstance(conn, math.ndarray): if (pre.num, post.num) != conn.shape: raise ModelBuildError( f'"conn" is provided as a matrix, and it is expected ' f'to be an array with shape of (pre.num, post.num) = ' f'{(pre.num, post.num)}, however we got {conn.shape}') self.conn = MatConn(conn_mat=conn) elif isinstance(conn, dict): if not ('i' in conn and 'j' in conn): raise ModelBuildError( f'"conn" is provided as a dict, and it is expected to ' f'be a dictionary with "i" and "j" specification, ' f'however we got {conn}') self.conn = IJConn(i=conn['i'], j=conn['j']) elif conn is None: self.conn = conn else: raise ModelBuildError(f'Unknown "conn" type: {conn}') # initialize # ---------- super(TwoEndConn, self).__init__(steps=steps, name=name)