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 __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, 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, progress_bar=True, optimize=True): self.closed = True # Start closed in case constructor raises exception self.progress_bar = progress_bar self.optimize = optimize 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 pt = ProgressTracker(progress_bar, Progress("Building", "Build")) with pt: if network is not None: # Build the network into the model self.model.build(network, progress=pt.next_stage("Building", "Build")) # Order the steps (they are made in `Simulator.reset`) self.dg = operator_dependency_graph(self.model.operators) if optimize: with pt.next_stage("Building (running optimizer)", "Optimization"): opmerge_optimize(self.model, self.dg) self._step_order = [ op for op in toposort(self.dg) if hasattr(op, "make_step") ] # -- map from Signal.base -> ndarray self.signals = SignalDict() for op in self.model.operators: op.init_signals(self.signals) # Add built states to the raw simulation data dictionary self._sim_data = self.model.params # Provide a nicer interface to simulation data self.data = SimulationData(self._sim_data) if seed is None: if network is not None and network.seed is not None: seed = network.seed + 1 else: seed = np.random.randint(npext.maxint) self.closed = False self.reset(seed=seed)
def noop_planner(operators): """Orders operators into a valid execution order, but does not perform any merging. Parameters ---------- operators : list of :class:`~nengo:nengo.builder.Operator` all the ``nengo`` operators in a model (unordered) Returns ------- list of tuple of :class:`~nengo:nengo.builder.Operator` operators in execution order """ dependency_graph = operator_dependency_graph(operators) plan = [(op,) for op in toposort(dependency_graph)] logger.debug("NOOP PLAN") logger.debug("\n" + "\n".join([str(x) for x in plan])) return plan
def noop_planner(operators): """ Orders operators into a valid execution order, but does not perform any merging. Parameters ---------- operators : list of `~nengo.builder.Operator` All the ``nengo`` operators in a model (unordered) Returns ------- plan : list of tuple of `~nengo.builder.Operator` Operators in execution order """ dependency_graph = operator_dependency_graph(operators) plan = [(op,) for op in toposort(dependency_graph)] logger.debug("NOOP PLAN") logger.debug("\n%s" * len(plan), *plan) return plan
def transitive_planner(op_list): """ Create merged execution plan through transitive closure construction. This is something like a middle ground between `.greedy_planner` and `.tree_planner`; it can improve simulation time over the greedy planner, but comes with potentially significant build time increases. Parameters ---------- op_list : list of `~nengo.builder.Operator` All the ``nengo`` operators in a model (unordered) Returns ------- plan : list of tuple of `~nengo.builder.Operator` Operators combined into mergeable groups and in execution order """ n_ele = len(op_list) merge_groups = {} dg = operator_dependency_graph(op_list) op_codes = {op: np.uint32(i) for i, op in enumerate(op_list)} dg = {op_codes[k]: set(op_codes[x] for x in v) for k, v in dg.items()} op_codes = {} # so it will get garbage collected dg = BidirectionalDAG(dg) # fail fast here if the op graph has cycles toposort(dg.forward) builder_types = [builder.Builder.builders[type(op)] for op in op_list] # sort operators by builder (we'll only be interested in one builder type # at a time, because we can't merge operators between builder types anyway) ops_by_type = defaultdict(set) for i, op in enumerate(op_list): ops_by_type[builder_types[i]].add(np.uint32(i)) # heuristic ordering for builder types (earlier items in the list will # have higher priority, meaning that we will choose to merge those ops # and potentially break lower-priority groups) order = [ op_builders.SparseDotIncBuilder, op_builders.ElementwiseIncBuilder, neuron_builders.SimNeuronsBuilder, process_builders.SimProcessBuilder, op_builders.SimPyFuncBuilder, learning_rule_builders.SimOjaBuilder, learning_rule_builders.SimVojaBuilder, learning_rule_builders.SimBCMBuilder, op_builders.CopyBuilder, op_builders.ResetBuilder, tensor_node.SimTensorNodeBuilder] for builder_type in order: if builder_type not in ops_by_type: # no ops of this type in the model continue ops = ops_by_type[builder_type] # compute transitive closure trans = [None for _ in range(n_ele)] transitive_closure_recurse(dg.forward, ops, trans, builder_type, builder_types, {}) # reduce it to the elements we care about (ops of the current # builder type) trans = {i: v for i, v in enumerate(trans[:len(op_list)]) if i in ops} while len(trans) > 0: # find all the ops that have no downstream dependents available = set(k for k, v in trans.items() if len(v) == 0) # sort those ops into mergeable groups groups = [] for op in available: for g in groups: if mergeable(op_list[op], (op_list[g[0]],)): g.append(op) break else: groups.append([op]) # merge the groups for g in groups: dg.merge(g, n_ele) merge_groups[n_ele] = g n_ele += 1 # remove those ops from the transitive closure for op in available: del trans[op] # remove those ops from the transitive closure of upstream ops # note: we first remove all the duplicate aliased transitive sets, # to reduce the number of set operations we need to do unique_trans = {id(v): v for v in trans.values()} for t in unique_trans.values(): t -= available # trans_reverse = [None for _ in range(n_ele)] # transitive_closure_recurse(dg.backward, ops, trans_reverse, # builder_type, builder_types, cache) # trans_reverse = {i: v for i, v in # enumerate(trans_reverse[:len(op_list)]) if i in ops} # group = None # for op in toposort(trans, trans_reverse): # if group is None: # group = [op] # continue # # if mergeable(op_list[op], (op_list[group[0]],)) and all( # x not in trans[op] for x in group): # group.append(op) # else: # dg.merge(group, n_ele) # merge_groups[n_ele] = group # n_ele += 1 # group = [op] # # dg.merge(group, n_ele) # merge_groups[n_ele] = group # n_ele += 1 del ops_by_type[builder_type] assert len(ops_by_type) == 0 # toposort the merged graph to come up with execution plan plan = toposort(dg.forward) plan = [tuple(op_list[x] for x in merge_groups[group]) for group in plan] logger.debug("TRANSITIVE PLAN") logger.debug("\n%s" * len(plan), *plan) return plan
def test_toposort(): edges = graphs.graph({'a': {'b', 'c'}, 'b': ('c', )}) assert graphs.toposort(edges) == ['a', 'b', 'c']
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 __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 __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): self.model = Model( dt=float(dt), label="Nengo RS model", decoder_cache=get_default_decoder_cache(), ) self.model.build(network) signal_to_engine_id = {} for signal_dict in self.model.sig.values(): for signal in signal_dict.values(): self.add_sig(signal_to_engine_id, signal) x = SignalU64("step", 0) signal_to_engine_id[self.model.step] = x signal_to_engine_id[self.model.time] = SignalF64("time", 0.0) self._sig_to_ngine_id = signal_to_engine_id dg = BidirectionalDAG(operator_dependency_graph(self.model.operators)) toposorted_dg = toposort(dg.forward) node_indices = {node: idx for idx, node in enumerate(toposorted_dg)} ops = [] for op in toposorted_dg: dependencies = [node_indices[node] for node in dg.backward[op]] if isinstance(op, core_op.Reset): ops.append( Reset( np.asarray(op.value, dtype=np.float64), self.get_sig(signal_to_engine_id, op.dst), dependencies, )) elif isinstance(op, core_op.TimeUpdate): ops.append( TimeUpdate( dt, self.get_sig(signal_to_engine_id, self.model.step), self.get_sig(signal_to_engine_id, self.model.time), dependencies, )) elif isinstance(op, core_op.ElementwiseInc): ops.append( ElementwiseInc( self.get_sig(signal_to_engine_id, op.Y), self.get_sig(signal_to_engine_id, op.A), self.get_sig(signal_to_engine_id, op.X), dependencies, )) elif isinstance(op, core_op.Copy): assert op.src_slice is None and op.dst_slice is None ops.append( Copy( op.inc, self.get_sig(signal_to_engine_id, op.src), self.get_sig(signal_to_engine_id, op.dst), dependencies, )) elif isinstance(op, core_op.DotInc): ops.append( DotInc( self.get_sig(signal_to_engine_id, op.Y), self.get_sig(signal_to_engine_id, op.A), self.get_sig(signal_to_engine_id, op.X), dependencies, )) elif isinstance(op, neurons.SimNeurons): signals = SignalDict() op.init_signals(signals) ops.append( SimNeurons( self.dt, op.neurons.step_math, [signals[s] for s in op.states] if hasattr(op, "states") else [], self.get_sig(signal_to_engine_id, op.J), self.get_sig(signal_to_engine_id, op.output), dependencies, )) elif isinstance(op, processes.SimProcess): signals = SignalDict() op.init_signals(signals) shape_in = (0, ) if op.input is None else op.input.shape shape_out = op.output.shape rng = None state = {k: signals[s] for k, s in op.state.items()} step_fn = op.process.make_step(shape_in, shape_out, self.dt, rng, state) ops.append( SimProcess( op.mode == "inc", lambda *args, step_fn=step_fn: np.asarray( step_fn(*args), dtype=float), self.get_sig(signal_to_engine_id, op.t), self.get_sig(signal_to_engine_id, op.output), None if op.input is None else self.get_sig( signal_to_engine_id, op.input), dependencies, )) elif isinstance(op, core_op.SimPyFunc): ops.append( SimPyFunc( lambda *args, op=op: np.asarray(op.fn(*args), dtype=float), self.get_sig(signal_to_engine_id, op.output), None if op.t is None else self.get_sig( signal_to_engine_id, op.t), None if op.x is None else self.get_sig( signal_to_engine_id, op.x), dependencies, )) else: raise Exception(f"missing: {op}") self.probe_mapping = {} for probe in self.model.probes: self.probe_mapping[probe] = Probe( signal_to_engine_id[self.model.sig[probe]["in"]]) self._engine = Engine(list(signal_to_engine_id.values()), ops, list(self.probe_mapping.values())) self.data = SimData(self) print("initialized") self._engine.reset()
def test_toposort(): edges = graphs.graph({"a": {"b", "c"}, "b": {"c"}, "c": set()}) assert graphs.toposort(edges) == ["a", "b", "c"]
def test_toposort(): edges = graphs.graph({'a': {'b', 'c'}, 'b': {'c'}, 'c': set()}) assert graphs.toposort(edges) == ['a', 'b', 'c']
def test_toposort(): edges = graphs.graph({'a': set(['b', 'c']), 'b': ('c',)}) assert graphs.toposort(edges) == ['a', 'b', 'c']
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): """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 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()