def step(self, state_or_history_browser, step_profile): ''' Perform a step of the simulation. The step profile will specify which parameters to pass to the simpack's step function. ''' auto_clock_generator = AutoClockGenerator() if isinstance(state_or_history_browser, garlicsim.data_structures.State): state = state_or_history_browser else: state = state_or_history_browser.get_last_state() auto_clock_generator.make_clock(state) if self.simple_step_defined: step_function = self.simpack.history_step if \ self.history_dependent else self.simpack.step result = step_function(state_or_history_browser, *step_profile.args, **step_profile.kwargs) else:# self.step_generator_defined is True step_generator = self.simpack.history_step_generator if \ self.history_dependent else \ self.simpack.step_generator iterator = step_generator(state_or_history_browser, *step_profile.args, **step_profile.kwargs) result = iterator.next() result.clock = AutoClockGenerator.make_clock(result) return result
class StepGeneratorIterator(BaseStepIterator): ''' An iterator that uses a simpack's step generator to produce states. A step iterator uses the simpack's original step function (or in this case generator) under the hood. The step iterator automatically adds `.clock` readings if the states produced by the step function are missing them. If the simpack's step generator will terminate, this iterator will make a fresh one without alerting the user. ''' def __init__(self, state, step_profile): self.current_state = state ''' The current state that will be crunched from on the next iteration. ''' self.step_function = step_profile.step_function '''The step generator that will `yield` states for us.''' self.step_profile = step_profile ''' The step profile which contains the arguments given to step function. ''' self.auto_clock_generator = AutoClockGenerator() '''Auto-clock generator which ensures all states have `.clock`.''' self.__build_raw_generator() self.auto_clock_generator.make_clock(self.current_state) def __build_raw_generator(self): '''Build a raw generator which will provide the states for us.''' self.raw_generator = self.step_profile.step_function( self.current_state, *self.step_profile.args, **self.step_profile.kwargs) def next(self): '''Crunch the next state.''' try: try: self.current_state = self.raw_generator.next() except StopIteration: self.__build_raw_generator() self.current_state = self.raw_generator.next() except StopIteration: raise SimpackError('The step generator %s raised ' '`StopIteration` without yielding even one ' 'state.' % self.step_profile.step_function) self._auto_clock(self.current_state) return self.current_state def _auto_clock(self, state): '''If the state has no clock reading, give it one automatically.''' state.clock = self.auto_clock_generator.make_clock(state)
def __init__(self, state, step_profile): self.current_state = state ''' The current state that will be crunched from on the next iteration. ''' self._state_of_raw_generator = None assert garlicsim.misc.simpack_grokker.step_types.InplaceStepGenerator.\ __instancecheck__(step_profile.step_function) self.step_function = step_profile.step_function '''The step function that will perform step for us.''' self.step_profile = step_profile ''' The step profile which contains the arguments given to step function. ''' self.auto_clock_generator = AutoClockGenerator(detect_static=True) '''Auto-clock generator which ensures all states have good `.clock`.''' self.auto_clock_generator.make_clock(self.current_state) self.__build_raw_generator()
class DuplicatingStepIterator(BaseStepIterator): ''' An iterator that uses a simpack's inplace step function to produce states. Despite the fact that this iterator uses an *inplace* step function under the hood, it produces a new distinct state on every iteration. It does that by deepcopying the state on every iteration. The step iterator automatically increments the state's `.clock` by 1 if the original step function doesn't change the `.clock` itself. ''' def __init__(self, state, step_profile): self.current_state = state ''' The current state that will be crunched from on the next iteration. ''' assert garlicsim.misc.simpack_grokker.step_types.InplaceStep.\ __instancecheck__(step_profile.step_function) self.step_function = step_profile.step_function '''The step function that will perform step for us.''' self.step_profile = step_profile ''' The step profile which contains the arguments given to step function. ''' self.auto_clock_generator = AutoClockGenerator(detect_static=True) '''Auto-clock generator which ensures all states have `.clock`.''' self.auto_clock_generator.make_clock(self.current_state) def next(self): '''Crunch the next state.''' new_state = \ garlicsim.misc.state_deepcopy.state_deepcopy(self.current_state) return_value = self.step_function(new_state, *self.step_profile.args, **self.step_profile.kwargs) assert return_value is None self._auto_clock(new_state) self.current_state = new_state return self.current_state def _auto_clock(self, state): ''' If the step function didn't advance the state's clock, advance it by 1. ''' state.clock = self.auto_clock_generator.make_clock(state)
class InplaceStepIterator(BaseStepIterator): ''' Step iterator that uses an inplace step function to perform step in place. A step iterator uses the simpack's original step function (in this case inplace step function) under the hood. This is an *inplace* step iterator; it doesn't produce new states, it modifies an existing one in place. It keeps yielding the same state, except it modifies it on each iteration. The step iterator automatically increments the state's `.clock` by 1 if the original step function doesn't change the `.clock` itself. ''' def __init__(self, state, step_profile): self.current_state = state ''' The current state that will be crunched from on the next iteration. ''' assert garlicsim.misc.simpack_grokker.step_types.InplaceStep.\ __instancecheck__(step_profile.step_function) self.step_function = step_profile.step_function '''The step function that will perform step for us.''' self.step_profile = step_profile ''' The step profile which contains the arguments given to step function. ''' self.auto_clock_generator = AutoClockGenerator(detect_static=True) '''Auto-clock generator which ensures all states have good `.clock`.''' self.auto_clock_generator.make_clock(self.current_state) def __next__(self): '''Crunch the next state.''' return_value = self.step_function(self.current_state, *self.step_profile.args, **self.step_profile.kwargs) assert return_value is None self._auto_clock(self.current_state) return self.current_state def _auto_clock(self, state): ''' If the step function didn't advance the state's clock, advance it by 1. ''' state.clock = self.auto_clock_generator.make_clock(state)
class DuplicatingStepIterator(BaseStepIterator): ''' An iterator that uses a simpack's inplace step function to produce states. Despite the fact that this iterator uses an *inplace* step function under the hood, it produces a new distinct state on every iteration. It does that by deepcopying the state on every iteration. The step iterator automatically increments the state's `.clock` by 1 if the original step function doesn't change the `.clock` itself. ''' def __init__(self, state, step_profile): self.current_state = state ''' The current state that will be crunched from on the next iteration. ''' assert garlicsim.misc.simpack_grokker.step_types.InplaceStep.\ __instancecheck__(step_profile.step_function) self.step_function = step_profile.step_function '''The step function that will perform step for us.''' self.step_profile = step_profile ''' The step profile which contains the arguments given to step function. ''' self.auto_clock_generator = AutoClockGenerator(detect_static=True) '''Auto-clock generator which ensures all states have `.clock`.''' self.auto_clock_generator.make_clock(self.current_state) def __next__(self): '''Crunch the next state.''' new_state = \ garlicsim.misc.state_deepcopy.state_deepcopy(self.current_state) return_value = self.step_function(new_state, *self.step_profile.args, **self.step_profile.kwargs) assert return_value is None self._auto_clock(new_state) self.current_state = new_state return self.current_state def _auto_clock(self, state): ''' If the step function didn't advance the state's clock, advance it by 1. ''' state.clock = self.auto_clock_generator.make_clock(state)
class InplaceStepIterator(BaseStepIterator): ''' Step iterator that uses an inplace step function to perform step in place. A step iterator uses the simpack's original step function (in this case inplace step function) under the hood. This is an *inplace* step iterator; it doesn't produce new states, it modifies an existing one in place. It keeps yielding the same state, except it modifies it on each iteration. The step iterator automatically increments the state's `.clock` by 1 if the original step function doesn't change the `.clock` itself. ''' def __init__(self, state, step_profile): self.current_state = state ''' The current state that will be crunched from on the next iteration. ''' assert garlicsim.misc.simpack_grokker.step_types.InplaceStep.\ __instancecheck__(step_profile.step_function) self.step_function = step_profile.step_function '''The step function that will perform step for us.''' self.step_profile = step_profile ''' The step profile which contains the arguments given to step function. ''' self.auto_clock_generator = AutoClockGenerator(detect_static=True) '''Auto-clock generator which ensures all states have good `.clock`.''' self.auto_clock_generator.make_clock(self.current_state) def next(self): '''Crunch the next state.''' return_value = self.step_function(self.current_state, *self.step_profile.args, **self.step_profile.kwargs) assert return_value is None self._auto_clock(self.current_state) return self.current_state def _auto_clock(self, state): ''' If the step function didn't advance the state's clock, advance it by 1. ''' state.clock = self.auto_clock_generator.make_clock(state)
def __init__(self, state_or_history_browser, step_profile, simple_step = None, step_generator=None): ''' Contructor. The iterator will use either a simple step function or a step generator under the hood. You have to supply either one or the other, but not both. ''' assert [simple_step, step_generator].count(None) == 1 self.simple_step = simple_step self.step_generator = step_generator self.raw_iterator = None self.step_profile = copy.deepcopy(step_profile) self.history_dependent = isinstance(state_or_history_browser, garlicsim.misc.HistoryBrowser) if self.history_dependent: self.current_state = None self.history_browser = state_or_history_browser else: self.current_state = state_or_history_browser self.history_browser = None self.auto_clock_generator = AutoClockGenerator() if self.current_state: self.auto_clock_generator.make_clock(self.current_state) self.step_profile_changed = False
def __init__(self, state, step_profile): self.current_state = state ''' The current state that will be crunched from on the next iteration. ''' self.step_function = step_profile.step_function '''The step function that will produce states for us.''' self.step_profile = step_profile ''' The step profile which contains the arguments given to step function. ''' self.auto_clock_generator = AutoClockGenerator() '''Auto-clock generator which ensures all states have `.clock`.''' self.auto_clock_generator.make_clock(self.current_state)
def __init__(self, history_browser, step_profile): assert isinstance(history_browser, garlicsim.misc.BaseHistoryBrowser) self.history_browser = history_browser '''The history browser that the history step function will use.''' self.history_step_function = step_profile.step_function '''The history step function that will produce states for us.''' self.step_profile = step_profile ''' The step profile which contains the arguments given to step function. ''' self.auto_clock_generator = AutoClockGenerator() '''Auto-clock generator which ensures all states have `.clock`.''' self.auto_clock_generator.make_clock( self.history_browser.get_last_state())
class StepIterator(BaseStepIterator): ''' An iterator that uses a simpack's step function to produce states. The step iterator automatically adds `.clock` readings if the states produced by the step function are missing them. ''' def __init__(self, state, step_profile): self.current_state = state ''' The current state that will be crunched from on the next iteration. ''' self.step_function = step_profile.step_function '''The step function that will produce states for us.''' self.step_profile = step_profile ''' The step profile which contains the arguments given to step function. ''' self.auto_clock_generator = AutoClockGenerator() '''Auto-clock generator which ensures all states have `.clock`.''' self.auto_clock_generator.make_clock(self.current_state) def __next__(self): '''Crunch the next state.''' self.current_state = self.step_function(self.current_state, *self.step_profile.args, **self.step_profile.kwargs) self._auto_clock(self.current_state) return self.current_state def _auto_clock(self, state): '''If the state has no clock reading, give it one automatically.''' state.clock = self.auto_clock_generator.make_clock(state)
class StepIterator(BaseStepIterator): ''' An iterator that uses a simpack's step function to produce states. The step iterator automatically adds `.clock` readings if the states produced by the step function are missing them. ''' def __init__(self, state, step_profile): self.current_state = state ''' The current state that will be crunched from on the next iteration. ''' self.step_function = step_profile.step_function '''The step function that will produce states for us.''' self.step_profile = step_profile ''' The step profile which contains the arguments given to step function. ''' self.auto_clock_generator = AutoClockGenerator() '''Auto-clock generator which ensures all states have `.clock`.''' self.auto_clock_generator.make_clock(self.current_state) def next(self): '''Crunch the next state.''' self.current_state = self.step_function(self.current_state, *self.step_profile.args, **self.step_profile.kwargs) self._auto_clock(self.current_state) return self.current_state def _auto_clock(self, state): '''If the state has no clock reading, give it one automatically.''' state.clock = self.auto_clock_generator.make_clock(state)
class HistoryStepIterator(BaseStepIterator): ''' An iterator that uses a simpack's history step function to produce states. The step iterator automatically adds `.clock` readings if the states produced by the step function are missing them. ''' def __init__(self, history_browser, step_profile): assert isinstance(history_browser, garlicsim.misc.BaseHistoryBrowser) self.history_browser = history_browser '''The history browser that the history step function will use.''' self.history_step_function = step_profile.step_function '''The history step function that will produce states for us.''' self.step_profile = step_profile ''' The step profile which contains the arguments given to step function. ''' self.auto_clock_generator = AutoClockGenerator() '''Auto-clock generator which ensures all states have `.clock`.''' self.auto_clock_generator.make_clock( self.history_browser.get_last_state()) def next(self): '''Crunch the next state.''' state = self.history_step_function(self.history_browser, *self.step_profile.args, **self.step_profile.kwargs) self._auto_clock(state) return state def _auto_clock(self, state): '''If the state has no clock reading, give it one automatically.''' state.clock = self.auto_clock_generator.make_clock(state)
class HistoryStepIterator(BaseStepIterator): """ An iterator that uses a simpack's history step function to produce states. The step iterator automatically adds `.clock` readings if the states produced by the step function are missing them. """ def __init__(self, history_browser, step_profile): assert isinstance(history_browser, garlicsim.misc.BaseHistoryBrowser) self.history_browser = history_browser """The history browser that the history step function will use.""" self.history_step_function = step_profile.step_function """The history step function that will produce states for us.""" self.step_profile = step_profile """ The step profile which contains the arguments given to step function. """ self.auto_clock_generator = AutoClockGenerator() """Auto-clock generator which ensures all states have `.clock`.""" self.auto_clock_generator.make_clock(self.history_browser.get_last_state()) def next(self): """Crunch the next state.""" state = self.history_step_function(self.history_browser, *self.step_profile.args, **self.step_profile.kwargs) self._auto_clock(state) return state def _auto_clock(self, state): """If the state has no clock reading, give it one automatically.""" state.clock = self.auto_clock_generator.make_clock(state)
def __init__(self, history_browser, step_profile): assert isinstance(history_browser, garlicsim.misc.BaseHistoryBrowser) self.history_browser = history_browser """The history browser that the history step function will use.""" self.history_step_function = step_profile.step_function """The history step function that will produce states for us.""" self.step_profile = step_profile """ The step profile which contains the arguments given to step function. """ self.auto_clock_generator = AutoClockGenerator() """Auto-clock generator which ensures all states have `.clock`.""" self.auto_clock_generator.make_clock(self.history_browser.get_last_state())
class InplaceStepGeneratorIterator(BaseStepIterator): ''' Step iterator that uses an inplace step generator to perform step in place. A step iterator uses the simpack's original step function (or in this case inplace step generator) under the hood. This is an *inplace* step iterator; it doesn't produce new states, it modifies an existing one in place. It keeps yielding the same state, except it modifies it on each iteration. The step iterator automatically increments the state's `.clock` by 1 if the step generator doesn't change the `.clock` itself. If the simpack's step generator will terminate, this iterator will make a fresh one without alerting the user. ''' def __init__(self, state, step_profile): self.current_state = state ''' The current state that will be crunched from on the next iteration. ''' assert garlicsim.misc.simpack_grokker.step_types.InplaceStepGenerator.\ __instancecheck__(step_profile.step_function) self.step_function = step_profile.step_function '''The step function that will produce states for us.''' self.step_profile = step_profile ''' The step profile which contains the arguments given to step function. ''' self.auto_clock_generator = AutoClockGenerator(detect_static=True) '''Auto-clock generator which ensures all states have good `.clock`.''' self.auto_clock_generator.make_clock(self.current_state) self.__build_raw_generator() def __build_raw_generator(self): '''Build a raw generator which will perform steps for us.''' self.raw_generator = self.step_profile.step_function( self.current_state, *self.step_profile.args, **self.step_profile.kwargs) def next(self): '''Crunch the next state.''' try: try: yielded_value = self.raw_generator.next() except StopIteration: self.__build_raw_generator() yielded_value = self.raw_generator.next() assert yielded_value is None self._auto_clock(self.current_state) except StopIteration: raise SimpackError('The inplace step generator `%s` raised ' '`StopIteration` without yielding even ' 'once.' % self.step_profile.step_function) return self.current_state def _auto_clock(self, state): ''' If the raw generator didn't advance the state's clock, advance it by 1. ''' state.clock = self.auto_clock_generator.make_clock(state)
class StepIterator(object): ''' An iterator that uses a simpack's step to produce states. The StepIterator uses under the hood the simpack's step function, be it a simple step function or a step generator. Using a StepIterator instead of using the simpack's step has a few advantages: 1. The StepIterator automatically adds clock readings if the states are missing them. 2. It's possible to change the step profile while iterating. 3. Guaranteed infinite iterator, even if the simpack's iterator is finite. And possibly more. todo: make stuff private here? ''' def __init__(self, state_or_history_browser, step_profile, simple_step = None, step_generator=None): ''' Contructor. The iterator will use either a simple step function or a step generator under the hood. You have to supply either one or the other, but not both. ''' assert [simple_step, step_generator].count(None) == 1 self.simple_step = simple_step self.step_generator = step_generator self.raw_iterator = None self.step_profile = copy.deepcopy(step_profile) self.history_dependent = isinstance(state_or_history_browser, garlicsim.misc.HistoryBrowser) if self.history_dependent: self.current_state = None self.history_browser = state_or_history_browser else: self.current_state = state_or_history_browser self.history_browser = None self.auto_clock_generator = AutoClockGenerator() if self.current_state: self.auto_clock_generator.make_clock(self.current_state) self.step_profile_changed = False def __iter__(self): return self def next(self): '''Crunch the next state.''' self.current_state = self.__get_new_state() self.auto_clock(self.current_state) return self.current_state def __get_new_state(self): # todo: rename? '''Internal method to crunch the next state.''' if self.simple_step: thing = self.history_browser if self.history_dependent else \ self.current_state return self.simple_step(thing, *self.step_profile.args, **self.step_profile.kwargs) else: # self.step_generator is not None self.rebuild_raw_iterator_if_necessary() try: return self.raw_iterator.next() except StopIteration: try: self.rebuild_raw_iterator() return self.raw_iterator.next() except StopIteration: raise SimpackError('''Step generator's iterator raised StopIteration before producing a single state.''') def rebuild_raw_iterator_if_necessary(self): ''' Rebuild the internal iterator if necessary. This is relevant only when we're using a simpack's step generator and not its simple step. ''' if (self.raw_iterator is None) or self.step_profile_changed: self.rebuild_raw_iterator() self.step_profile_changed = False def rebuild_raw_iterator(self): ''' Rebuild the internal iterator. This is relevant only when we're using a simpack's step generator and not its simple step. ''' thing = self.current_state or self.history_browser self.raw_iterator = self.step_generator(thing, *self.step_profile.args, **self.step_profile.kwargs) def auto_clock(self, state): '''If the state has no clock reading, give it one automatically.''' state.clock = self.auto_clock_generator.make_clock(state) def set_step_profile(self, step_profile): ''' Set a new step profile for the StepIterator to use. The StepIterator will immediately adopt the new step profile, and any states that will be crunched from this point on will be crunched using the new step profile. (At least until it is changed again.) ''' self.step_profile = copy.deepcopy(step_profile) self.step_profile_changed = True
class StepGeneratorIterator(BaseStepIterator): ''' An iterator that uses a simpack's step generator to produce states. A step iterator uses the simpack's original step function (or in this case generator) under the hood. The step iterator automatically adds `.clock` readings if the states produced by the step function are missing them. If the simpack's step generator will terminate, this iterator will make a fresh one without alerting the user. ''' def __init__(self, state, step_profile): self.current_state = state ''' The current state that will be crunched from on the next iteration. ''' self.step_function = step_profile.step_function '''The step generator that will `yield` states for us.''' self.step_profile = step_profile ''' The step profile which contains the arguments given to step function. ''' self.auto_clock_generator = AutoClockGenerator() '''Auto-clock generator which ensures all states have `.clock`.''' self.__build_raw_generator() self.auto_clock_generator.make_clock(self.current_state) def __build_raw_generator(self): '''Build a raw generator which will provide the states for us.''' self.raw_generator = self.step_profile.step_function( self.current_state, *self.step_profile.args, **self.step_profile.kwargs ) def __next__(self): '''Crunch the next state.''' try: try: self.current_state = next(self.raw_generator) except StopIteration: self.__build_raw_generator() self.current_state = next(self.raw_generator) except StopIteration: raise SimpackError('The step generator %s raised ' '`StopIteration` without yielding even one ' 'state.' % self.step_profile.step_function) self._auto_clock(self.current_state) return self.current_state def _auto_clock(self, state): '''If the state has no clock reading, give it one automatically.''' state.clock = self.auto_clock_generator.make_clock(state)
class DuplicatingStepGeneratorIterator(BaseStepIterator): ''' An iterator that uses a simpack's inplace step generator to produce states. Despite the fact that this iterator uses an *inplace* step generator under the hood, it produces a new distinct state on every iteration. It does that by deepcopying the state on every iteration. The step iterator automatically increments the state's `.clock` by 1 if the original step generator doesn't change the `.clock` itself. ''' def __init__(self, state, step_profile): self.current_state = state ''' The current state that will be crunched from on the next iteration. ''' self._state_of_raw_generator = None assert garlicsim.misc.simpack_grokker.step_types.InplaceStepGenerator.\ __instancecheck__(step_profile.step_function) self.step_function = step_profile.step_function '''The step function that will perform step for us.''' self.step_profile = step_profile ''' The step profile which contains the arguments given to step function. ''' self.auto_clock_generator = AutoClockGenerator(detect_static=True) '''Auto-clock generator which ensures all states have good `.clock`.''' self.auto_clock_generator.make_clock(self.current_state) self.__build_raw_generator() def __build_raw_generator(self): '''Build a raw generator which will perform step for us.''' self._state_of_raw_generator = \ garlicsim.misc.state_deepcopy.state_deepcopy(self.current_state) self.raw_generator = self.step_profile.step_function( self._state_of_raw_generator, *self.step_profile.args, **self.step_profile.kwargs ) def next(self): '''Crunch the next state.''' try: try: yielded_value = self.raw_generator.next() except StopIteration: self.__build_raw_generator() yielded_value = self.raw_generator.next() assert yielded_value is None self._auto_clock(self._state_of_raw_generator) self.current_state = garlicsim.misc.state_deepcopy.state_deepcopy( self._state_of_raw_generator ) except StopIteration: raise SimpackError('The inplace step generator `%s` raised ' '`StopIteration` without yielding even ' 'once.' % self.step_profile.step_function) return self.current_state def _auto_clock(self, state): ''' If the raw generator didn't advance the state's clock, advance it by 1. ''' state.clock = self.auto_clock_generator.make_clock(state)
class InplaceStepGeneratorIterator(BaseStepIterator): ''' Step iterator that uses an inplace step generator to perform step in place. A step iterator uses the simpack's original step function (or in this case inplace step generator) under the hood. This is an *inplace* step iterator; it doesn't produce new states, it modifies an existing one in place. It keeps yielding the same state, except it modifies it on each iteration. The step iterator automatically increments the state's `.clock` by 1 if the step generator doesn't change the `.clock` itself. If the simpack's step generator will terminate, this iterator will make a fresh one without alerting the user. ''' def __init__(self, state, step_profile): self.current_state = state ''' The current state that will be crunched from on the next iteration. ''' assert garlicsim.misc.simpack_grokker.step_types.InplaceStepGenerator.\ __instancecheck__(step_profile.step_function) self.step_function = step_profile.step_function '''The step function that will produce states for us.''' self.step_profile = step_profile ''' The step profile which contains the arguments given to step function. ''' self.auto_clock_generator = AutoClockGenerator(detect_static=True) '''Auto-clock generator which ensures all states have good `.clock`.''' self.auto_clock_generator.make_clock(self.current_state) self.__build_raw_generator() def __build_raw_generator(self): '''Build a raw generator which will perform steps for us.''' self.raw_generator = self.step_profile.step_function( self.current_state, *self.step_profile.args, **self.step_profile.kwargs ) def next(self): '''Crunch the next state.''' try: try: yielded_value = self.raw_generator.next() except StopIteration: self.__build_raw_generator() yielded_value = self.raw_generator.next() assert yielded_value is None self._auto_clock(self.current_state) except StopIteration: raise SimpackError('The inplace step generator `%s` raised ' '`StopIteration` without yielding even ' 'once.' % self.step_profile.step_function) return self.current_state def _auto_clock(self, state): ''' If the raw generator didn't advance the state's clock, advance it by 1. ''' state.clock = self.auto_clock_generator.make_clock(state)