def create(self, otherchunk, actrvariables=None): """ Create (aka set) a chunk for manual control. The chunk is returned (and could be used by device or external environment). """ if actrvariables == None: actrvariables = {} try: mod_attr_val = { x[0]: utilities.check_bound_vars(actrvariables, x[1]) for x in otherchunk.removeunused() } #creates dict of attr-val pairs according to otherchunk except ACTRError as arg: raise ACTRError( "Setting the chunk '%s' in the manual buffer is impossible; %s" % (otherchunk, arg)) new_chunk = chunks.Chunk(self._MANUAL, **mod_attr_val) #creates new chunk if new_chunk.cmd.values not in utilities.CMDMANUAL: raise ACTRError( "Motor module received an invalid command: '%s'. The valid commands are: '%s'" % (new_chunk.cmd.values, utilities.CMDMANUAL)) if new_chunk.cmd.values == utilities.CMDPRESSKEY: pressed_key = new_chunk.key.values.upper( ) #change key into upper case mod_attr_val["key"] = pressed_key new_chunk = chunks.Chunk(self._MANUAL, **mod_attr_val) #creates new chunk if pressed_key not in self.LEFT_HAND and new_chunk.key.values not in self.RIGHT_HAND: raise ACTRError("Motor module received an invalid key: %s" % pressed_key) return new_chunk
def chunktype(cls_name, field_names, defaults=None): """ Creates type chunk. Works like namedtuple. For example: >>> chunktype('chunktype_example0', 'value') :param field_names: an iterable or a string of slot names separated by spaces :param defaults: default values for the slots, given as an iterable, counting from the last element """ if cls_name in utilities.SPECIALCHUNKTYPES and field_names != utilities.SPECIALCHUNKTYPES[cls_name]: raise ACTRError("You cannot redefine slots of the chunk type '%s'; you can only use the slots '%s'" % (cls_name, utilities.SPECIALCHUNKTYPES[cls_name])) try: field_names = field_names.replace(',', ' ').split() except AttributeError: # no .replace or .split pass # assume it's already a sequence of identifiers field_names = tuple(sorted(name + "_" for name in field_names)) for each in field_names: if each == "ISA" or each == "isa": raise ACTRError("You cannot use the slot 'isa' in your chunk. That slot is used to define chunktypes.") try: Chunk._chunktypes.update({cls_name:collections.namedtuple(cls_name, field_names, defaults=defaults)}) #chunktypes are not returned; they are stored as Chunk class attribute except TypeError: Chunk._chunktypes.update({cls_name:collections.namedtuple(cls_name, field_names)}) #chunktypes are not returned; they are stored as Chunk class attribute
def chunktype(cls_name, field_names, verbose=False): """ Creates type chunk. Works like namedtuple. For example: >>> chunktype('chunktype_example0', 'value') """ if cls_name in utilities.SPECIALCHUNKTYPES and field_names != utilities.SPECIALCHUNKTYPES[ cls_name]: raise ACTRError( "You cannot redefine slots of the chunk type '%s'; you can only use the slots '%s'" % (cls_name, utilities.SPECIALCHUNKTYPES[cls_name])) try: field_names = field_names.replace(',', ' ').split() except AttributeError: # no .replace or .split pass # assume it's already a sequence of identifiers field_names = tuple(sorted(name + "_" for name in field_names)) for each in field_names: if each == "ISA" or each == "isa": raise ACTRError( "You cannot use the slot 'isa' in your chunk. That slot is used to define chunktypes." ) Chunk._chunktypes.update({ cls_name: collections.namedtuple(cls_name, field_names, verbose=verbose) }) #chunktypes are not returned; they are stored as Chunk class attribute
def default_harvest(self, value): try: self.dm = value except ValueError: raise ACTRError( 'The default harvest set in the goal buffer is not a possible declarative memory' )
def update(self, RHSdictionary, time): """ Update buffers (RHS of production rules). """ temp_actrvariables = dict(self.__actrvariables) ordering_dict = {"!": 0, "?": 0, "=": 1, "@": 2, "*": 3, "+": 4, "~": 5} try: dictionary = collections.OrderedDict.fromkeys(sorted(RHSdictionary, key=lambda x:ordering_dict[x[0]])) except KeyError: raise ACTRError("The RHS rule '%s' is invalid; every condition in RHS rules must start with one of these signs: %s" % (self.used_rulename, list(self._RHSCONVENTIONS.keys()))) dictionary.update(RHSdictionary) for key in dictionary: submodule_name = key[1:] #this is the name of updated submodule code = key[0] #this is what the key should do try: temp_actrvariables.pop("=" + submodule_name) #pop used submodule (needed for strict harvesting) except KeyError: pass updated = self.buffers[submodule_name] production = getattr(self, self._RHSCONVENTIONS[code])(submodule_name, updated, dictionary[key], self.__actrvariables, time) updated.state = updated._BUSY if production.__name__ in self._INTERRUPTIBLE: self.procs.append((submodule_name, production)) else: yield from production #this either moves production on to modify, retrieve etc. (see RHSCONVENTIONS for the list), or it appends that process to the processes that have to be done as an extra process by model, i.e., not directly by productions (distinction made as in ACT-R) #this last part is strict harvesting if self.model_parameters["strict_harvesting"]: for key in temp_actrvariables: submodule_name = key[1:] if submodule_name in self.buffers: self.procs.append((submodule_name, self.clear(submodule_name, self.buffers[submodule_name], None, self.__actrvariables, time)))
def shift(self, otherchunk, harvest=None, actrvariables=None): """ Return a chunk, time needed to attend and shift eye focus to the chunk, and the landing site of eye mvt. """ if actrvariables == None: actrvariables = {} try: mod_attr_val = {x[0]: utilities.check_bound_vars(actrvariables, x[1]) for x in otherchunk.removeunused()} #creates dict of attr-val pairs according to otherchunk except ACTRError as arg: raise ACTRError("The chunk '%s' is not defined correctly; %s" % (otherchunk, arg)) vis_delay = None for each in self.environment.stimulus: try: if self.environment.stimulus[each]['position'] == (float(mod_attr_val['screen_pos'].screen_x), float(mod_attr_val['screen_pos'].screen_y)): mod_attr_val['value'] = self.environment.stimulus[each]['text'] vis_delay = self.environment.stimulus[each].get('vis_delay') except (AttributeError, KeyError): raise ACTRError("The chunk in the visual buffer is not defined correctly. It is not possible to move attention.") new_chunk = chunks.Chunk(self._VISUAL, **mod_attr_val) #creates new chunk if new_chunk.cmd not in utilities.CMDVISUAL: raise ACTRError("Visual module received an invalid command: '%s'. The valid commands are: '%s'" % (new_chunk.cmd, utilities.CMDVISUAL)) if new_chunk.cmd == utilities.CMDMOVEATTENTION and self.model_parameters['emma']: angle_distance = utilities.calculate_visual_angle(self.current_focus, [float(new_chunk.screen_pos.screen_x), float(new_chunk.screen_pos.screen_y)], self.environment.size, self.environment.simulated_screen_size, self.environment.viewing_distance) encoding_time = utilities.calculate_delay_visual_attention(angle_distance=angle_distance, K=self.model_parameters["eye_mvt_scaling_parameter"], k=self.model_parameters['eye_mvt_angle_parameter'], emma_noise=self.model_parameters['emma_noise'], vis_delay=vis_delay) preparation_time = utilities.calculate_preparation_time(emma_noise=self.model_parameters['emma_noise']) execution_time = utilities.calculate_execution_time(angle_distance, emma_noise=self.model_parameters['emma_noise']) landing_site = utilities.calculate_landing_site([float(new_chunk.screen_pos.screen_x), float(new_chunk.screen_pos.screen_y)], angle_distance, emma_landing_site_noise=self.model_parameters['emma_landing_site_noise']) elif new_chunk.cmd == utilities.CMDMOVEATTENTION and not self.model_parameters['emma']: encoding_time = 0.085 preparation_time = 0 execution_time = 0.085 landing_site = (float(new_chunk.screen_pos.screen_x), float(new_chunk.screen_pos.screen_y)) else: raise ACTRError("Visual module received an invalid command: '%s'. The only valid command currently is: %s" % (new_chunk.cmd, utilities.CMDMOVEATTENTION)) return new_chunk, (encoding_time, preparation_time, execution_time), landing_site
def shift(self, otherchunk, harvest=None, actrvariables=None, model_parameters=None): """ Return a chunk, time needed to attend and shift eye focus to the chunk, and the landing site of eye mvt. """ if model_parameters == None: model_parameters = {} model_parameters = model_parameters.copy() model_parameters.update(self.model_parameters) if actrvariables == None: actrvariables = {} try: mod_attr_val = {x[0]: utilities.check_bound_vars(actrvariables, x[1]) for x in otherchunk.removeunused()} except ACTRError as arg: raise ACTRError("Shifting towards the chunk '%s' is impossible; %s" % (otherchunk, arg)) vis_delay = None for each in self.environment.stimulus: try: if self.environment.stimulus[each]['position'] == (float(mod_attr_val['screen_pos'].values.screen_x.values), float(mod_attr_val['screen_pos'].values.screen_y.values)): mod_attr_val['value'] = self.environment.stimulus[each]['text'] vis_delay = self.environment.stimulus[each].get('vis_delay') except (AttributeError, KeyError): raise ACTRError("The chunk in the visual buffer is not defined correctly. It is not possible to move attention.") new_chunk = chunks.Chunk(self._VISUAL, **mod_attr_val) #creates new chunk if model_parameters['emma']: angle_distance = utilities.calculate_visual_angle(self.environment.current_focus, [float(new_chunk.screen_pos.values.screen_x.values), float(new_chunk.screen_pos.values.screen_y.values)], self.environment.size, self.environment.simulated_screen_size, self.environment.viewing_distance) encoding_time = utilities.calculate_delay_visual_attention(angle_distance=angle_distance, K=model_parameters["eye_mvt_scaling_parameter"], k=model_parameters['eye_mvt_angle_parameter'], emma_noise=model_parameters['emma_noise'], vis_delay=vis_delay) preparation_time = utilities.calculate_preparation_time(emma_noise=model_parameters['emma_noise'], emma_preparation_time=model_parameters['emma_preparation_time']) execution_time = utilities.calculate_execution_time(angle_distance, emma_noise=model_parameters['emma_noise']) landing_site = utilities.calculate_landing_site([float(new_chunk.screen_pos.values.screen_x.values), float(new_chunk.screen_pos.values.screen_y.values)], angle_distance, emma_landing_site_noise=model_parameters['emma_landing_site_noise']) elif not model_parameters['emma']: encoding_time = 0.085 preparation_time = 0 execution_time = 0.085 landing_site = (float(new_chunk.screen_pos.values.screen_x.values), float(new_chunk.screen_pos.values.screen_y.values)) return new_chunk, (encoding_time, preparation_time, execution_time), landing_site
def retrieveorset(self, name, updated, otherchunk, temp_actrvariables, time): """ Find out whether a buffer should be set (for buffers that are not attached to any dm, i.e., Goal or Motor or Vision) or should trigger retrieval. """ updated.state = updated._BUSY if isinstance(updated, goals.Goal): yield from self.clear(name, updated, otherchunk, temp_actrvariables, time, freeing=False) extra_time = utilities.calculate_setting_time(updated) time += extra_time yield Event(roundtime(time), name, self._UNKNOWN) if self.model_parameters['production_compilation']: RHSdict = otherchunk._asdict() RHSdict = {item[0]: item[1] for item in RHSdict.items()} self.current_slotvals[name] = RHSdict updated.create(otherchunk, list(self.dm.values())[0], temp_actrvariables) created_elem = list(updated)[0] updated.state = updated._FREE yield Event(roundtime(time), name, "CREATED A CHUNK: %s" % str(created_elem)) elif isinstance(updated, vision.VisualLocation): extra_time = utilities.calculate_setting_time(updated) time += extra_time #0 ms to create chunk in location (pop-up effect) yield Event(roundtime(time), name, self._UNKNOWN) chunk, stim = updated.find(otherchunk, actrvariables=temp_actrvariables, extra_tests=self.extra_tests.get(name, {})) #extra_time currently ignored if chunk: yield from self.clear(name, updated, None, temp_actrvariables, time, freeing=False) updated.add(chunk, stim, time) updated.state = updated._FREE else: updated.state = updated._ERROR yield Event(roundtime(time), name, "ENCODED LOCATION:'%s'" % str(chunk)) elif isinstance(updated, vision.Visual): mod_attr_val = {x[0]: utilities.check_bound_vars(temp_actrvariables, x[1]) for x in otherchunk.removeunused()} if (not mod_attr_val['cmd'].values) or mod_attr_val['cmd'].values not in utilities.CMDVISUAL: raise ACTRError("Visual module received no command or an invalid command: '%s'. The valid commands are: '%s'" % (mod_attr_val['cmd'].values, utilities.CMDVISUAL)) if mod_attr_val['cmd'].values == utilities.CMDMOVEATTENTION: ret = yield from self.visualshift(name, updated, otherchunk, temp_actrvariables, time) return ret #visual action returns value, namely, its continuation method elif mod_attr_val['cmd'].values == utilities.CMDCLEAR: updated.stop_automatic_buffering() updated.state = updated._FREE yield Event(roundtime(time), name, "VISUAL STOPPED FROM AUTOMATIC BUFFERING AT ITS CURRENT FOCUS") elif isinstance(updated, motor.Motor): ret = yield from self.motorset(name, updated, otherchunk, temp_actrvariables, time) return ret #motor action returns value, namely, its continuation method else: yield from self.retrieve(name, updated, otherchunk, temp_actrvariables, time)
def LHStest(self, dictionary, actrvariables, update=False): """ Test rules in LHS of production rules. update specifies whether actrvariables should be updated (this does not happen when rules are tested, only when they are fired) """ for key in dictionary: submodule_name = key[1:] #this is the module code = key[0] #this is what the module should do; standardly, query, i.e., ?, or test, = if code not in self._LHSCONVENTIONS: raise ACTRError("The LHS rule '%s' is invalid; every condition in LHS rules must start with one of these signs: %s" % (self.used_rulename, list(self._LHSCONVENTIONS.keys()))) result = getattr(self, self._LHSCONVENTIONS[code])(submodule_name, self.buffers.get(submodule_name), dictionary[key], actrvariables) if not result[0]: return False else: actrvariables.update(result[1]) if update: self.__actrvariables = actrvariables return True
def clear(self, name, cleared, optional, temp_actrvariables, time, freeing=True): """ Clear a buffer. The 'freeing' argument specifies whether the state should be considered FREE (if the rule is run alone) or not (if it is embedded in another rule). """ cleared.state = cleared._BUSY try: cleared.clear(time) #clear the buffer; works for decl. mem. buffers (tied to a specific decl. mem) except AttributeError: #unless it fails because the cleared chunk cannot be added anywhere if len(self.dm) == 1: cleared.clear(time, list(self.dm.values())[0]) #if there is only one memory, add the chunk there elif optional: cleared.clear(time, self.dm[optional]) #if not, optional must specify memory where chunk should be added else: try: cleared.clear(time, self.dm[name]) #if nothing else works, check whether buffer instance was bound to a decl. mem by user except KeyError: raise ACTRError("It is not specified to what memory the buffer %s should be cleared" % name) yield Event(roundtime(time), name, "CLEARED") if freeing: cleared.state = cleared._FREE