def _configure_history(self, initial_conditions): """ Set initial conditions for the simulation using either the provided initial_conditions or, if none are provided, the model's initial() method. This method is called durin the Simulator's __init__(). Any initial_conditions that are provided as an argument are expected to have dimensions 1, 2, and 3 with shapse corresponding to the number of state_variables, nodes and modes, respectively. If the provided inital_conditions are shorter in time (dim=0) than the required history the model's initial() method is called to make up the difference. """ rng = numpy.random if hasattr(self.integrator, 'noise'): rng = self.integrator.noise.random_stream # Default initial conditions if initial_conditions is None: n_time, n_svar, n_node, n_mode = self.good_history_shape LOG.info( 'Preparing initial history of shape %r using model.initial()', self.good_history_shape) if self.surface is not None: n_node = self.number_of_nodes history = self.model.initial(self.integrator.dt, (n_time, n_svar, n_node, n_mode), rng) # ICs provided else: # history should be [timepoints, state_variables, nodes, modes] LOG.info('Using provided initial history of shape %r', initial_conditions.shape) n_time, n_svar, n_node, n_mode = ic_shape = initial_conditions.shape nr = self.connectivity.number_of_regions if self.surface is not None and n_node == nr: initial_conditions = initial_conditions[:, :, self._regmap] return self._configure_history(initial_conditions) elif ic_shape[1:] != self.good_history_shape[1:]: raise_value_error( "Incorrect history sample shape %s, expected %s" % ic_shape[1:], self.good_history_shape[1:]) else: if ic_shape[0] >= self.horizon: LOG.debug("Using last %d time-steps for history.", self.horizon) history = initial_conditions[ -self.horizon:, :, :, :].copy() else: LOG.debug('Padding initial conditions with model.initial') history = self.model.initial(self.integrator.dt, self.good_history_shape, rng) shift = self.current_step % self.horizon history = numpy.roll(history, -shift, axis=0) history[:ic_shape[0], :, :, :] = initial_conditions history = numpy.roll(history, shift, axis=0) self.current_step += ic_shape[0] - 1 # Make sure that history values are bounded, # and any possible non-state variables are initialized # based on state variable ones (but with no coupling yet...) self._update_and_bound_history(numpy.swapaxes(history, 0, 1)) LOG.info('Final initial history shape is %r', history.shape) # create initial state from history self.current_state = history[self.current_step % self.horizon].copy() LOG.debug('initial state has shape %r' % (self.current_state.shape, )) if self.surface is not None and history.shape[ 2] > self.connectivity.number_of_regions: n_reg = self.connectivity.number_of_regions (nt, ns, _, nm), ax = history.shape, (2, 0, 1, 3) region_history = numpy.zeros((nt, ns, n_reg, nm)) numpy_add_at(region_history.transpose(ax), self._regmap, history.transpose(ax)) region_history /= numpy.bincount(self._regmap).reshape((-1, 1)) history = region_history # create history query implementation self.history = SparseHistory(self.connectivity.weights, self.connectivity.idelays, self.model.cvar, self.model.number_of_modes) # initialize its buffer self.history.initialize(history)
def config_for_sim(self, simulator): "Configure projection matrix monitor for given simulation." super(Projection, self).config_for_sim(simulator) self._sim = simulator if hasattr(self, 'sensors'): self.sensors.configure() # handle region vs simulation, analytic vs numerical proj, cortical vs subcortical. # setup convenient locals surf = simulator.surface conn = simulator.connectivity using_cortical_surface = surf is not None if using_cortical_surface: non_cortical_indices, = numpy.where( numpy.bincount(surf.region_mapping) == 1) self.rmap = surf.region_mapping else: # assume all cortical if no info if conn.cortical.size == 0: conn.cortical = numpy.array([True] * conn.weights.shape[0]) non_cortical_indices, = numpy.where(~conn.cortical) if self.region_mapping is None: raise Exception( "Please specify a region mapping on the EEG/MEG/iEEG monitor when " "performing a region simulation.") else: self.rmap = self.region_mapping LOG.debug( 'Projection used in region sim has %d non-cortical regions', non_cortical_indices.size) have_subcortical = len(non_cortical_indices) > 0 # determine source space if using_cortical_surface: sources = {'loc': surf.vertices, 'ori': surf.vertex_normals} else: sources = { 'loc': conn.centres[conn.cortical], 'ori': conn.orientations[conn.cortical] } # compute analytic if not provided if self.projection is None: print("projection is None") LOG.debug( 'Precomputed projection not unavailable using analytic approximation.' ) self.gain = self.analytic(**sources) else: self.gain = self.projection.projection_data # reduce to region lead field if region sim if not using_cortical_surface and self.gain.shape[ 1] == self.rmap.mapping.size: gain = numpy.zeros((self.gain.shape[0], conn.number_of_regions)) numpy_add_at(gain.T, self.rmap.mapping, self.gain.T) LOG.debug('Region mapping gain shape %s to %s', self.gain.shape, gain.shape) self.gain = gain # append analytic sub-cortical to lead field if have_subcortical: # need matrix of shape (proj.shape[0], len(sc_ind)) src = conn.centres[non_cortical_indices], conn.orientations[ non_cortical_indices] self.gain = numpy.hstack((self.gain, self.analytic(*src))) LOG.debug('Added subcortical analytic gain, for final shape %s', self.gain.shape) if self.sensors.usable is not None and not self.sensors.usable.all(): mask_unusable = ~self.sensors.usable self.gain[mask_unusable] = 0.0 LOG.debug('Zeroed gain coefficients for %d unusable sensors', mask_unusable.sum()) # unconditionally zero NaN elements; framework not prepared for NaNs. nan_mask = numpy.isfinite(self.gain).all(axis=1) self.gain[~nan_mask] = 0.0 LOG.debug('Zeroed %d NaN gain coefficients', nan_mask.sum()) # attrs used for recording self._state = numpy.zeros((self.gain.shape[0], len(self.voi))) self._period_in_steps = int(self.period / self.dt) LOG.debug('State shape %s, period in steps %s', self._state.shape, self._period_in_steps) LOG.info('Projection configured gain shape %s', self.gain.shape)
def __call__(self, simulation_length=None, random_state=None): """ When a Simulator is called it returns an iterator. kwargs: ``simulation_length``: total time of simulation ``random_state``: a state for the NumPy random number generator, saved from a previous call to permit consistent continuation of a simulation. """ self.calls += 1 self.simulation_length = simulation_length or self.simulation_length # Estimate run time and storage requirements, with logging. self._guesstimate_runtime() self._calculate_storage_requirement() if random_state is not None: if isinstance(self.integrator, integrators_module.IntegratorStochastic): self.integrator.noise.random_stream.set_state(random_state) msg = "random_state supplied with seed %s" LOG.info(msg, self.integrator.noise.random_stream.get_state()[1][0]) else: LOG.warn( "random_state supplied for non-stochastic integration") # number of steps to perform integrqtion int_steps = int(simulation_length / self.integrator.dt) msg = 'sim length %f ms requires %d steps' LOG.info(msg, simulation_length, int_steps) # locals for cleaner code. ncvar = len(self.model.cvar) number_of_regions = self.connectivity.number_of_regions # Exact dtypes and alignment are required by c speedups. Once we have history objects these will be encapsulated # cvar index array broadcastable to nodes, cvars, nodes cvar = numpy.array(self.model.cvar[numpy.newaxis, :, numpy.newaxis], dtype=numpy.intc) LOG.debug("%s: cvar is: %s" % (str(self), str(cvar))) # idelays array broadcastable to nodes, cvars, nodes idelays = numpy.array(self.connectivity.idelays[:, numpy.newaxis, :], dtype=numpy.intc, order='c') LOG.debug("%s: idelays shape is: %s" % (str(self), str(idelays.shape))) # weights array broadcastable to nodes, cva, nodes, modes weights = self.connectivity.weights[:, numpy.newaxis, :, numpy.newaxis] LOG.debug("%s: weights shape is: %s" % (str(self), str(weights.shape))) # node_ids broadcastable to nodes, cvars, nodes node_ids = numpy.array( numpy.arange(number_of_regions)[numpy.newaxis, numpy.newaxis, :], dtype=numpy.intc) LOG.debug("%s: node_ids shape is: %s" % (str(self), str(node_ids.shape))) if self.surface is None: local_coupling = 0.0 else: (nt, ns, _, nm), ax = self.history.shape, (2, 0, 1, 3) region_history = numpy.zeros((nt, ns, number_of_regions, nm)) numpy_add_at(region_history.transpose(ax), self._regmap, self.history.transpose(ax)) region_history /= numpy.bincount(self._regmap).reshape((-1, 1)) if self.surface.coupling_strength.size == 1: local_coupling = (self.surface.coupling_strength[0] * self.surface.local_connectivity.matrix) elif self.surface.coupling_strength.size == self.surface.number_of_vertices: ind = numpy.arange(self.number_of_nodes, dtype=int) vec_cs = numpy.zeros((self.number_of_nodes, )) vec_cs[:self.surface. number_of_vertices] = self.surface.coupling_strength sp_cs = sparse.csc_matrix( (vec_cs, (ind, ind)), shape=(self.number_of_nodes, self.number_of_nodes)) local_coupling = sp_cs * self.surface.local_connectivity.matrix if self.stimulus is None: stimulus = 0.0 else: self.stimulus.configure_time( numpy.r_[:simulation_length:self.integrator.dt].reshape( (1, -1))) stimulus = numpy.zeros((self.model.nvar, self.number_of_nodes, 1)) LOG.debug("stimulus shape is: %s", stimulus.shape) # initial state, history[timepoint[0], state_variables, nodes, modes] state = self.history[self.current_step % self.horizon, :] LOG.debug("state shape is: %s", state.shape) delayed_state = numpy.zeros( (number_of_regions, ncvar, number_of_regions, self.model.number_of_modes)) # integration loop for step in xrange(self.current_step + 1, self.current_step + int_steps + 1): # compute afferent coupling time_indices = (step - 1 - idelays) % self.horizon if self.surface is None: get_state(self.history, time_indices, cvar, node_ids, out=delayed_state) node_coupling = self.coupling(weights, state[self.model.cvar], delayed_state) else: get_state(region_history, time_indices, cvar, node_ids, out=delayed_state) region_coupling = self.coupling( weights, region_history[(step - 1) % self.horizon, self.model.cvar], delayed_state) node_coupling = region_coupling[:, self._regmap].transpose( (1, 0, 2)) # stimulus pattern at this time point if self.stimulus is not None: stim_step = step - (self.current_step + 1 ) # TODO stim_step != current step ?? stimulus[self.model.cvar, :, :] = self.stimulus( stim_step).reshape((1, -1, 1)) # apply integration scheme state = self.integrator.scheme(state, self.model.dfun, node_coupling, local_coupling, stimulus) # update full history & region history if applicable self.history[step % self.horizon, :] = state if self.surface is not None: region_state = numpy.zeros( (number_of_regions, state.shape[0], state.shape[2])) numpy_add_at(region_state, self._regmap, state.transpose((1, 0, 2))) region_state /= numpy.bincount(self._regmap).reshape( (-1, 1, 1)) region_history[step % self.horizon, :] = region_state.transpose( (1, 0, 2)) # record monitor output & forward to caller output = [monitor.record(step, state) for monitor in self.monitors] if any(outputi is not None for outputi in output): yield output # This -1 is here for not repeating the point on resume self.current_step = self.current_step + int_steps - 1