Example #1
0
    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
Example #2
0
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)
Example #3
0
 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()
Example #4
0
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)
Example #7
0
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)
Example #8
0
    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
Example #9
0
 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)
Example #10
0
    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)
Example #12
0
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)
Example #13
0
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())
 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)
Example #17
0
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)
Example #18
0
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)
Example #20
0
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)