def __init__( self, network, dt=0.001, seed=None, model=None, progress_bar=True): self.closed = False self.progress_bar = progress_bar if model is None: self._model = Model(dt=float(dt), label="%s, dt=%f" % (network, dt), decoder_cache=get_default_decoder_cache()) else: self._model = model if network is not None: # Build the network into the model self._model.build(network, progress_bar=self.progress_bar) # -- map from Signal.base -> ndarray self.signals = SignalDict() for op in self._model.operators: op.init_signals(self.signals) # Order the steps (they are made in `Simulator.reset`) self.dg = operator_depencency_graph(self._model.operators) self._step_order = [op for op in toposort(self.dg) if hasattr(op, 'make_step')] # Add built states to the probe dictionary self._probe_outputs = self._model.params # Provide a nicer interface to probe outputs self.data = ProbeDict(self._probe_outputs) seed = np.random.randint(npext.maxint) if seed is None else seed self.reset(seed=seed)
def __init__(self, network, dt=0.001, seed=None, model=None): self.closed = False if model is None: dt = float(dt) # make sure it's a float (for division purposes) self.model = Model(dt=dt, label="%s, dt=%f" % (network, dt), decoder_cache=get_default_decoder_cache()) else: self.model = model if network is not None: # Build the network into the model self.model.build(network) self.model.decoder_cache.shrink() # -- map from Signal.base -> ndarray self.signals = SignalDict() for op in self.model.operators: op.init_signals(self.signals) # Order the steps (they are made in `Simulator.reset`) self.dg = operator_depencency_graph(self.model.operators) self._step_order = [op for op in toposort(self.dg) if hasattr(op, 'make_step')] # Add built states to the probe dictionary self._probe_outputs = self.model.params # Provide a nicer interface to probe outputs self.data = ProbeDict(self._probe_outputs) seed = np.random.randint(npext.maxint) if seed is None else seed self.reset(seed=seed)
def __init__(self, model, dt=0.001, seed=None, builder=Builder()): # Call the builder to build the model self.model = builder(model, dt) self.dt = dt # Use model seed as simulator seed if the seed is not provided # Note: seed is not used right now, but one day... self.seed = self.model.seed if seed is None else seed # -- map from Signal.base -> ndarray self.signals = SignalDict(__time__=np.asarray(0.0, dtype=np.float64)) for op in self.model.operators: op.init_signals(self.signals, self.dt) self.dg = operator_depencency_graph(self.model.operators) self._step_order = [node for node in toposort(self.dg) if hasattr(node, 'make_step')] self._steps = [node.make_step(self.signals, self.dt) for node in self._step_order] self.n_steps = 0 # Add built states to the probe dictionary self._probe_outputs = self.model.params # Provide a nicer interface to probe outputs self.data = ProbeDict(self._probe_outputs)
def exact_dependency_graph(operators): from nengo.utils.simulator import operator_depencency_graph edges = operator_depencency_graph(operators) dg = nx.DiGraph() dg.add_nodes_from(operators) # add all operators for source, dests in edges.items(): dg.add_edges_from((source, dest) for dest in dests) return dg
def __init__(self, network, dt=0.001, seed=None, model=None): """Initialize the simulator with a network and (optionally) a model. Most of the time, you will pass in a network and sometimes a dt:: sim1 = nengo.Simulator(my_network) # Uses default 0.001s dt sim2 = nengo.Simulator(my_network, dt=0.01) # Uses 0.01s dt For more advanced use cases, you can initialize the model yourself, and also pass in a network that will be built into the same model that you pass in:: sim = nengo.Simulator(my_network, model=my_model) If you want full control over the build process, then you can build your network into the model manually. If you do this, then you must explicitly pass in ``None`` for the network:: sim = nengo.Simulator(None, model=my_model) Parameters ---------- network : nengo.Network instance or None A network object to the built and then simulated. If a fully built ``model`` is passed in, then you can skip building the network by passing in network=None. dt : float, optional The length of a simulator timestep, in seconds. seed : int, optional A seed for all stochastic operators used in this simulator. model : nengo.builder.Model instance or None, optional A model object that contains build artifacts to be simulated. Usually the simulator will build this model for you; however, if you want to build the network manually, or to inject some build artifacts in the Model before building the network, then you can pass in a ``nengo.builder.Model`` instance. """ if model is None: dt = float(dt) # make sure it's a float (for division purposes) self.model = Model(dt=dt, label="%s, dt=%f" % (network, dt), decoder_cache=get_default_decoder_cache()) else: self.model = model if network is not None: # Build the network into the model self.model.build(network) self.model.decoder_cache.shrink() # -- map from Signal.base -> ndarray self.signals = SignalDict(__time__=np.asarray(0.0, dtype=np.float64)) for op in self.model.operators: op.init_signals(self.signals) # Order the steps (they are made in `Simulator.reset`) self.dg = operator_depencency_graph(self.model.operators) self._step_order = [op for op in toposort(self.dg) if hasattr(op, 'make_step')] # Add built states to the probe dictionary self._probe_outputs = self.model.params # Provide a nicer interface to probe outputs self.data = ProbeDict(self._probe_outputs) seed = np.random.randint(npext.maxint) if seed is None else seed self.reset(seed=seed)
def __init__(self, network, dt=0.001, seed=None, model=None): """Initialize the simulator with a network and (optionally) a model. Most of the time, you will pass in a network and sometimes a dt:: sim1 = nengo.Simulator(my_network) # Uses default 0.001s dt sim2 = nengo.Simulator(my_network, dt=0.01) # Uses 0.01s dt For more advanced use cases, you can initialize the model yourself, and also pass in a network that will be built into the same model that you pass in:: sim = nengo.Simulator(my_network, model=my_model) If you want full control over the build process, then you can build your network into the model manually. If you do this, then you must explicitly pass in ``None`` for the network:: sim = nengo.Simulator(None, model=my_model) Parameters ---------- network : nengo.Network instance or None A network object to the built and then simulated. If a fully built ``model`` is passed in, then you can skip building the network by passing in network=None. dt : float The length of a simulator timestep, in seconds. seed : int A seed for all stochastic operators used in this simulator. Note that there are not stochastic operators implemented currently, so this parameters does nothing. model : nengo.builder.Model instance or None A model object that contains build artifacts to be simulated. Usually the simulator will build this model for you; however, if you want to build the network manually, or to inject some build artifacts in the Model before building the network, then you can pass in a ``nengo.builder.Model`` instance. """ self.dt = dt if model is None: self.model = Model(dt=self.dt, label="%s, dt=%f" % (network.label, dt), seed=network.seed) else: self.model = model if network is not None: # Build the network into the model Builder.build(network, model=self.model) # Use model seed as simulator seed if the seed is not provided # Note: seed is not used right now, but one day... self.seed = self.model.seed if seed is None else seed # -- map from Signal.base -> ndarray self.signals = SignalDict(__time__=np.asarray(0.0, dtype=np.float64)) for op in self.model.operators: op.init_signals(self.signals, self.dt) self.dg = operator_depencency_graph(self.model.operators) self._step_order = [node for node in toposort(self.dg) if hasattr(node, "make_step")] self._steps = [node.make_step(self.signals, self.dt) for node in self._step_order] self.n_steps = 0 # Add built states to the probe dictionary self._probe_outputs = self.model.params # Provide a nicer interface to probe outputs self.data = ProbeDict(self._probe_outputs)
def greedy_planner(operators): from nengo.utils.simulator import operator_depencency_graph edges = operator_depencency_graph(operators) for op, dests in iteritems(edges): assert is_op(op) and all(is_op(op2) for op2 in dests) # map unscheduled ops to their direct predecessors and successors predecessors_of = {} successors_of = {} for op in operators: predecessors_of[op] = set() successors_of[op] = set() for op, dests in iteritems(edges): for op2 in dests: predecessors_of[op2].add(op) successors_of[op].update(dests) # available ops are ready to be scheduled (all predecessors scheduled) available = defaultdict(set) for op in (op for op, dep in iteritems(predecessors_of) if not dep): available[type(op)].add(op) rval = [] while len(predecessors_of) > 0: if len(available) == 0: raise ValueError("Cycles in the op graph") chosen_type = sorted(available.items(), key=lambda x: len(x[1]))[-1][0] candidates = available[chosen_type] # --- greedily pick non-overlapping ops chosen = [] base_sets = defaultdict(set) base_incs = defaultdict(set) base_updates = defaultdict(set) def overlaps(op): for s in op.sets: if any(s.may_share_memory(s2) for s2 in base_sets[s.base]): return True for s in op.incs: if any(s.may_share_memory(s2) for s2 in base_incs[s.base]): return True for s in op.updates: if any(s.may_share_memory(s2) for s2 in base_updates[s.base]): return True return False for op in candidates: if not overlaps(op): # add op chosen.append(op) for s in op.sets: base_sets[s.base].add(s) for s in op.incs: base_incs[s.base].add(s) for s in op.updates: base_updates[s.base].add(s) # --- schedule ops assert chosen rval.append((chosen_type, chosen)) # --- update predecessors and successors of unsheduled ops available[chosen_type].difference_update(chosen) if not available[chosen_type]: del available[chosen_type] for op in chosen: for op2 in successors_of[op]: preds = predecessors_of[op2] preds.remove(op) if len(preds) == 0: available[type(op2)].add(op2) del predecessors_of[op] del successors_of[op] assert len(operators) == sum(len(p[1]) for p in rval) # print('greedy_planner: Program len:', len(rval)) return rval
def finalize_build(self): """ Finalize the build step. Called once the MpiBuilder has finished running. Finalizes operators and probes, converting them to strings. Then writes all relevant information (signals, ops and probes for each component) to an HDF5 file. Then, if self.mpi_sim is not None (so we want to create a runnable MPI simulator), calls self.mpi_sim.load_file, which tells the C++ code to load the HDF5 file we have just written and create a working simulator. """ all_ops = list(chain(*[self.component_ops[component] for component in range(self.n_components)])) dg = operator_depencency_graph(all_ops) global_ordering = [op for op in toposort(dg) if hasattr(op, "make_step")] self.global_ordering = {op: i for i, op in enumerate(global_ordering)} self._finalize_ops() self._finalize_probes() with h5.File(self.save_file_name, "w") as save_file: save_file.attrs["dt"] = self.dt save_file.attrs["n_components"] = self.n_components for component in range(self.n_components): component_group = save_file.create_group(str(component)) # signals signals = self.signals[component] signal_dset = component_group.create_dataset( "signals", (self.total_signal_size[component],), dtype="float64", compression=self.h5_compression ) offset = 0 for key, sig in signals: A = sig.base._value if A.ndim == 0: A = np.reshape(A, (1, 1)) if A.dtype != np.float64: A = A.astype(np.float64) signal_dset[offset : offset + A.size] = A.flatten() offset += A.size # signal keys component_group.create_dataset( "signal_keys", data=[long(key) for key, sig in signals], dtype="int64", compression=self.h5_compression, ) # signal shapes def pad(x): return (1, 1) if len(x) == 0 else ((x[0], 1) if len(x) == 1 else x) component_group.create_dataset( "signal_shapes", data=np.array([pad(sig.shape) for key, sig in signals]), dtype="u2", compression=self.h5_compression, ) # signal_labels signal_labels = [str(p[1]) for p in signals] store_string_list(component_group, "signal_labels", signal_labels, compression=self.h5_compression) # operators op_strings = self.op_strings[component] store_string_list(component_group, "operators", op_strings, compression=self.h5_compression) # probes probe_strings = self.probe_strings[component] store_string_list(component_group, "probes", probe_strings, compression=self.h5_compression) probe_strings = self.probe_strings[component] store_string_list(save_file, "probe_info", self.all_probe_strings, compression=self.h5_compression) if self.mpi_sim is not None: self.mpi_sim.load_network(self.save_file_name) os.remove(self.save_file_name) for args in self.pyfunc_args: f = { "N": self.mpi_sim.create_PyFunc, "I": self.mpi_sim.create_PyFuncI, "O": self.mpi_sim.create_PyFuncO, "IO": self.mpi_sim.create_PyFuncIO, }[args[0]] f(*args[1:]) self.mpi_sim.finalize_build()
def __init__(self, network, dt=0.001, seed=None, model=None, dtype=rc.get('precision', 'dtype')): """Initialize the simulator with a network and (optionally) a model. Most of the time, you will pass in a network and sometimes a dt:: sim1 = nengo.Simulator(my_network) # Uses default 0.001s dt sim2 = nengo.Simulator(my_network, dt=0.01) # Uses 0.01s dt For more advanced use cases, you can initialize the model yourself, and also pass in a network that will be built into the same model that you pass in:: sim = nengo.Simulator(my_network, model=my_model) If you want full control over the build process, then you can build your network into the model manually. If you do this, then you must explicitly pass in ``None`` for the network:: sim = nengo.Simulator(None, model=my_model) Parameters ---------- network : nengo.Network instance or None A network object to the built and then simulated. If a fully built ``model`` is passed in, then you can skip building the network by passing in network=None. dt : float The length of a simulator timestep, in seconds. seed : int A seed for all stochastic operators used in this simulator. Note that there are not stochastic operators implemented currently, so this parameters does nothing. model : nengo.builder.Model instance or None A model object that contains build artifacts to be simulated. Usually the simulator will build this model for you; however, if you want to build the network manually, or to inject some build artifacts in the Model before building the network, then you can pass in a ``nengo.builder.Model`` instance. """ dt = float(dt) # make sure it's a float (for division purposes) if model is None: self.model = Model(dt=dt, label="%s, dt=%f" % (network, dt), decoder_cache=get_default_decoder_cache(), dtype=dtype) else: self.model = model #print(network) if network is not None: # Build the network into the model self.model.build(network) self.model.decoder_cache.shrink() self.seed = np.random.randint(npext.maxint) if seed is None else seed self.rng = np.random.RandomState(self.seed) # -- map from Signal.base -> ndarray self.signals = SignalDict( __time__=np.asarray(npext.castDecimal(0), dtype=self.dtype)) #print(self.model) #print(self.model.operators) for op in self.model.operators: op.init_signals(self.signals) self.dg = operator_depencency_graph(self.model.operators) self._step_order = [ node for node in toposort(self.dg) if hasattr(node, 'make_step') ] self._steps = [ node.make_step(self.signals, dt, self.rng) for node in self._step_order ] # Add built states to the probe dictionary self._probe_outputs = self.model.params # Provide a nicer interface to probe outputs self.data = ProbeDict(self._probe_outputs) self.reset()
def __init__(self, network, dt=0.001, seed=None, model=None): """Initialize the simulator with a network and (optionally) a model. Most of the time, you will pass in a network and sometimes a dt:: sim1 = nengo.Simulator(my_network) # Uses default 0.001s dt sim2 = nengo.Simulator(my_network, dt=0.01) # Uses 0.01s dt For more advanced use cases, you can initialize the model yourself, and also pass in a network that will be built into the same model that you pass in:: sim = nengo.Simulator(my_network, model=my_model) If you want full control over the build process, then you can build your network into the model manually. If you do this, then you must explicitly pass in ``None`` for the network:: sim = nengo.Simulator(None, model=my_model) Parameters ---------- network : nengo.Network instance or None A network object to the built and then simulated. If a fully built ``model`` is passed in, then you can skip building the network by passing in network=None. dt : float The length of a simulator timestep, in seconds. seed : int A seed for all stochastic operators used in this simulator. Note that there are not stochastic operators implemented currently, so this parameters does nothing. model : nengo.builder.Model instance or None A model object that contains build artifacts to be simulated. Usually the simulator will build this model for you; however, if you want to build the network manually, or to inject some build artifacts in the Model before building the network, then you can pass in a ``nengo.builder.Model`` instance. """ self.dt = dt if model is None: self.model = Model(dt=self.dt, label="%s, dt=%f" % (network.label, dt), seed=network.seed) else: self.model = model if network is not None: # Build the network into the model Builder.build(network, model=self.model) # Use model seed as simulator seed if the seed is not provided # Note: seed is not used right now, but one day... self.seed = self.model.seed if seed is None else seed # -- map from Signal.base -> ndarray self.signals = SignalDict(__time__=np.asarray(0.0, dtype=np.float64)) for op in self.model.operators: op.init_signals(self.signals, self.dt) self.dg = operator_depencency_graph(self.model.operators) self._step_order = [node for node in toposort(self.dg) if hasattr(node, 'make_step')] self._steps = [node.make_step(self.signals, self.dt) for node in self._step_order] self.n_steps = 0 # Add built states to the probe dictionary self._probe_outputs = self.model.params # Provide a nicer interface to probe outputs self.data = ProbeDict(self._probe_outputs)
def finalize_build(self): """ Finalize the build step. Called once the MpiBuilder has finished running. Finalizes operators and probes, converting them to strings. Then writes all relevant information (signals, ops and probes for each component) to an HDF5 file. Then, if self.native_sim is not None (so we want to create a runnable MPI simulator), calls self.native_sim.load_file which tells the C++ code to load the HDF5 file we have just written and create a working simulator. """ all_ops = list(chain( *[self.component_ops[component] for component in range(self.n_components)])) dg = operator_depencency_graph(all_ops) global_ordering = [ op for op in toposort(dg) if hasattr(op, 'make_step')] self.global_ordering = {op: i for i, op in enumerate(global_ordering)} self.global_ordering[self.time_update] = -1 # Needs to be done after calling operator_depencency_graph. # operator_depencency_graph will detect an error caused # by multiple sets of the time signal for component in range(self.n_components): self.assign_ops(component, [self.time_update]) self._finalize_ops() self._finalize_probes() with h5.File(self.save_file, 'w') as save_file: save_file.attrs['dt'] = self.dt save_file.attrs['n_components'] = self.n_components for component in range(self.n_components): component_group = save_file.create_group(str(component)) # base signals base_signals = self.base_signals[component] signal_dset = component_group.create_dataset( 'signals', (self.total_base_signal_size[component],), dtype='float64', compression=self.h5_compression) offset = 0 for base in base_signals.values(): shape = base.shape stride = base.elemstrides if base.ndim == 2: # assert that the signal is contiguous assert ((stride[1] == 1 and shape[1] == stride[0]) or (stride[0] == 1 and shape[0] == stride[1])) if base.elemstrides[1] == 1: values = base.initial_value.flatten() elif base.elemstrides[0] == 1: values = base.initial_value.T.flatten() else: raise ValueError( "Received a signal with strides that " "nengo_mpi cannot handle. Signal " "was %s, stride is %s." % ( base, base.elemstrides)) signal_dset[offset:offset+base.size] = values else: # assert that the signal is contiguous assert base.ndim == 0 or stride[0] == 1 signal_dset[ offset:offset+base.size] = base.initial_value offset += base.size # base signal keys base_signal_keys = np.array([ long(key) for key in base_signals.keys()]) component_group.create_dataset( 'signal_keys', data=base_signal_keys, dtype='int64', compression=self.h5_compression) # base signal shapes base_signal_shapes = np.array([ pad(sig.shape) for sig in base_signals.values()]) component_group.create_dataset( 'signal_shapes', data=base_signal_shapes, dtype='int64', compression=self.h5_compression) # base signal strides base_signal_strides = np.array([ pad(sig.elemstrides) for sig in base_signals.values()]) component_group.create_dataset( 'signal_strides', data=base_signal_strides, dtype='int64', compression=self.h5_compression) # base signal labels if self.debug: signal_labels = [sig.name for sig in base_signals.values()] else: signal_labels = ['' for sig in base_signals.values()] store_string_list( component_group, 'signal_labels', signal_labels, compression=self.h5_compression) # operators op_strings = self.op_strings[component] store_string_list( component_group, 'operators', op_strings, compression=self.h5_compression) # probes probe_strings = self.probe_strings[component] store_string_list( component_group, 'probes', probe_strings, compression=self.h5_compression) store_string_list( save_file, 'probe_info', self.all_probe_strings, compression=self.h5_compression) if self.native_sim is not None: self.native_sim.load_network(self.save_file) os.remove(self.save_file) for op in self.pyfunc_ops: self.native_sim.create_PyFunc(op, self.global_ordering[op]) self.native_sim.finalize_build()