Exemple #1
0
    def _parse_stimulus_values(self, NAME, stimulus_values, variables,
                               to_be_continued, is_appending):
        if not self.val[kw.STIMULUS_ELEMENTS]:
            return f"The parameter 'stimulus_elements' must be assigned before the parameter '{NAME}'."

        # Create and populate the struct with None values
        if not is_appending:
            self.val[NAME] = dict()
            for e in self.val[kw.STIMULUS_ELEMENTS]:
                self.val[NAME][e] = None
            self.val[NAME][kw.DEFAULT] = None

        single_w, _ = ParseUtil.evaluate(stimulus_values, variables)
        if single_w is not None:
            if is_appending:
                return "A single value for '{}' cannot follow other values.".format(
                    NAME)
            elif to_be_continued:
                return "A single value for '{}' cannot be followed by other values.".format(
                    NAME)
            else:
                for key in self.val[NAME]:
                    self.val[NAME][key] = single_w
                self.val[NAME].pop(kw.DEFAULT)
        else:
            ws = ParseUtil.comma_split(stimulus_values)
            ws = [x.strip() for x in ws]
            for e_w_str in ws:  # eb_w_str is 'e:value' or 'default:value'
                if e_w_str.count(':') != 1:
                    return "Expected 'element:value' or 'default:value' in '{}', got '{}'.".format(
                        NAME, e_w_str)
                e, w_str = e_w_str.split(':')
                e = e.strip()
                w_str = w_str.strip()
                w, err = ParseUtil.evaluate(w_str, variables)
                if err:
                    return "Invalid value '{}' for '{}' in parameter '{}'.".format(
                        w_str, e, NAME)

                if e == kw.DEFAULT:
                    if self.val[NAME][kw.DEFAULT] is not None:
                        return "Default value for '{}' can only be stated once.".format(
                            NAME)
                elif e not in self.val[kw.STIMULUS_ELEMENTS]:
                    return f"Error in parameter '{NAME}': '{e}' is an invalid stimulus element."
                if self.val[NAME][e] is not None:
                    return "Duplicate of {} in '{}'.".format(e, NAME)
                self.val[NAME][e] = w

            if not to_be_continued:
                # Set the default value for non-set stimulus elements
                err = self._set_default_values(NAME)
                if err:
                    return err

        return None  # No error
Exemple #2
0
    def _parse_behavior_cost(self, behavior_cost_str, variables,
                             to_be_continued, is_appending):
        if not self.val[kw.BEHAVIORS]:
            return f"The parameter 'behaviors' must be assigned before the parameter '{kw.BEHAVIOR_COST}'."

        # Create and populate the struct with None values
        if not is_appending:
            self.val[kw.BEHAVIOR_COST] = dict()
            for e in self.val[kw.BEHAVIORS]:
                self.val[kw.BEHAVIOR_COST][e] = None
            self.val[kw.BEHAVIOR_COST][kw.DEFAULT] = None

        single_c, _ = ParseUtil.evaluate(behavior_cost_str, variables)
        if single_c is not None:
            if is_appending:
                return "A single value for '{}' cannot follow other values.".format(
                    kw.BEHAVIOR_COST)
            elif to_be_continued:
                return "A single value for '{}' cannot be followed by other values.".format(
                    kw.BEHAVIOR_COST)
            else:
                for key in self.val[kw.BEHAVIOR_COST]:
                    self.val[kw.BEHAVIOR_COST][key] = single_c
                self.val[kw.BEHAVIOR_COST].pop(kw.DEFAULT)
        else:
            cs = ParseUtil.comma_split(behavior_cost_str)
            cs = [x.strip() for x in cs]
            for bc_str in cs:  # bc_str is 'e:value' or 'default:value'
                if bc_str.count(':') != 1:
                    return "Expected 'element:value' or 'default:value' in '{}', got '{}'.".format(
                        kw.BEHAVIOR_COST, bc_str)
                b, c_str = bc_str.split(':')
                b = b.strip()
                c_str = c_str.strip()
                c, err = ParseUtil.evaluate(c_str, variables)
                if err:
                    return f"Invalid value '{c_str}' for '{b}' in parameter '{kw.BEHAVIOR_COST}'."

                if b == kw.DEFAULT:
                    if self.val[kw.BEHAVIOR_COST][kw.DEFAULT] is not None:
                        return "Default value for '{}' can only be stated once.".format(
                            kw.BEHAVIOR_COST)
                elif b not in self.val[kw.BEHAVIORS]:
                    return f"Error in parameter '{kw.BEHAVIOR_COST}': '{b}' is an invalid behavior name."
                if self.val[kw.BEHAVIOR_COST][b] is not None:
                    return "Duplicate of {} in '{}'.".format(
                        b, kw.BEHAVIOR_COST)
                self.val[kw.BEHAVIOR_COST][b] = c

            if not to_be_continued:
                # Set the default value for non-set behaviors
                err = self._set_default_values(kw.BEHAVIOR_COST)
                if err:
                    return err

        return None  # No error
Exemple #3
0
 def is_met(self, variables, event_counter):
     ismet, err = ParseUtil.evaluate(self.cond, variables, event_counter, ParseUtil.STOP_COND)
     if err:
         raise ParseException(self.lineno, err)
     if type(ismet) is not bool:
         raise ParseException(self.lineno, f"Condition '{self.cond}' is not a boolean expression.")
     return ismet
Exemple #4
0
    def _goto_if_met(self, variables):
        goto_prob_cumsum = list()
        cumsum = 0
        for i in range(len(self.goto)):
            prob = self.goto[i][0]
            if type(prob) is str:
                self.goto[i][0], err = ParseUtil.evaluate(prob, variables=variables)
                if err:
                    raise ParseException(self.lineno, err)
            cumsum += self.goto[i][0]
            goto_prob_cumsum.append(cumsum)
        if cumsum > 1:
            raise ParseException(self.lineno, f"Sum of probabilities is {cumsum}>1.")

        ind = ParseUtil.weighted_choice(goto_prob_cumsum)
        if ind is None:
            return None
        else:
            return self.goto[ind][1]
Exemple #5
0
    def is_met(self, response, global_variables, local_variables, event_counter):
        variables_both = Variables.join(global_variables, local_variables)
        if self.condition is None:
            ismet = True
        elif self.condition_is_behavior:
            ismet = (self.condition == response)
        else:
            ismet, err = ParseUtil.evaluate(self.condition, variables_both, event_counter,
                                            ParseUtil.PHASE_LINE)
            if err:
                raise ParseException(self.lineno, err)
            if type(ismet) is not bool:
                raise ParseException(self.lineno, f"Condition '{self.condition}' is not a boolean expression.")

        if ismet:
            label = self._goto_if_met(variables_both)
            if label is None:  # In "ROW1(0.1),ROW2(0.3)", goto_if_met returns None with prob. 0.6
                ismet = False
        else:
            label = None
        return ismet, label
Exemple #6
0
    def _perform_action(self, lineno, action):
        """
        Sets a variable (x:3) or count_reset(event).
        """
        omit_learn = False

        # No action to perform
        if len(action) == 0:
            pass

        # Setting a variable
        elif action.count(':') == 1 or action.count('=') == 1:
            if action.count('=') == 1:
                sep = '='
            else:
                sep = ':'
            var_name, value_str = ParseUtil.split1_strip(action, sep=sep)
            variables_join = Variables.join(self.global_variables, self.local_variables)
            value, err = ParseUtil.evaluate(value_str, variables_join)
            if err:
                raise ParseException(lineno, err)
            err = self.local_variables.set(var_name, value, self.parameters)
            if err:
                raise ParseException(lineno, err)

        # count_reset
        elif action.startswith("count_reset(") and action.endswith(")"):
            event = action[12:-1]
            self.event_counter.reset_count(event)

        # omit_learn
        elif action == "@omit_learn":
            omit_learn = True

        else:
            raise ParseException(lineno, "Internal error.")  # Should have been caught during Pase.parse()

        return omit_learn
Exemple #7
0
    def str_set(self,
                prop,
                v_str,
                variables,
                phases,
                all_run_labels,
                to_be_continued,
                is_appending=False):
        """
        Parses the specified value (as a string) of the specified parameter and sets the resulting
        value. The input variables is a Variables object.

        Returns error message if parsing failed.
        """
        err = check_is_parameter_name(prop)
        if err:
            return err

        # all_phase_labels = phases.labels_set()
        if prop == kw.BEHAVIORS:
            return self._parse_behaviors(v_str, variables, is_appending)

        elif prop == kw.STIMULUS_ELEMENTS:
            return self._parse_stimulus_elements(v_str, variables,
                                                 is_appending)

        elif prop == kw.MECHANISM_NAME:
            return self._parse_mechanism_name(v_str)

        elif prop in (kw.START_VSS, kw.ALPHA_VSS):
            return self._parse_alphastart_vss(prop, v_str, variables,
                                              to_be_continued, is_appending)

        elif prop in (kw.START_W, kw.ALPHA_W, kw.U, kw.LAMBDA):
            return self._parse_stimulus_values(prop, v_str, variables,
                                               to_be_continued, is_appending)

        elif prop in (kw.BETA, kw.MU, kw.START_V, kw.ALPHA_V):
            return self._parse_stimulus_response_values(
                prop, v_str, variables, to_be_continued, is_appending)

        # Float
        elif prop in (kw.DISCOUNT, kw.TRACE):
            v, err = ParseUtil.evaluate(v_str, variables)
            if err:
                return err
            if (v < 0) or (v > 1):
                return f"Parameter '{prop}' must be a number >=0 and <=1."
            self.val[prop] = v
            return None

        elif prop == kw.BEHAVIOR_COST:
            return self._parse_behavior_cost(v_str, variables, to_be_continued,
                                             is_appending)

        elif prop == kw.RESPONSE_REQUIREMENTS:
            return self._parse_response_requirements(v_str, to_be_continued,
                                                     is_appending)

        # 'on' or 'off'
        elif prop in (kw.BIND_TRIALS, kw.CUMULATIVE):
            v_str_lower = v_str.lower()
            if v_str_lower not in ('on', 'off'):
                return "Parameter '{}' must be 'on' or 'off'.".format(prop)
            self.val[prop] = v_str_lower
            return None

        # Positive integer
        elif prop == kw.N_SUBJECTS:
            v, err = ParseUtil.parse_posint(v_str, variables)
            if err:
                return err
            if not v:
                return "Parameter {} must be a positive integer.".format(
                    kw.N_SUBJECTS)
            self.val[kw.N_SUBJECTS] = v
            return None

        # Any nonempty (after strip) string
        elif prop in (kw.TITLE, kw.SUBPLOTTITLE):
            if to_be_continued:  # Add the removed comma
                v_str = v_str + ","
            self.val[prop] = v_str
            return None

        # 'average', 'all' or 1-based index
        elif prop == kw.SUBJECT:
            return self._parse_subject(v_str, variables)

        # 'all' or s1->b1->s2->..., s=se1,se2,...
        elif prop == kw.XSCALE:
            return self._parse_xscale(v_str, phases)

        # 'subset' or 'exact'
        elif prop in (kw.MATCH, kw.XSCALE_MATCH):
            if v_str.lower() not in ('subset', 'exact'):
                return "Parameter {} must be 'subset' or 'exact'.".format(prop)
            self.val[prop] = v_str
            return None

        # 'all' or cs-list of phase labels
        elif prop == kw.PHASES:
            return self._parse_phases(v_str)  # , all_phase_labels)

        # String (@run-labels) (for postprocessing)
        elif prop == kw.RUNLABEL:
            if v_str not in all_run_labels:
                return "Invalid @RUN-label {}".format(v_str)
            self.val[kw.RUNLABEL] = v_str
            return None

        # Valid path to writable file
        elif prop == kw.FILENAME:
            filename = v_str
            try:
                file = open(filename, 'w', newline='')
            except Exception as ex:
                return str(ex)
            finally:
                file.close()
                try:
                    os.remove(filename)
                except FileNotFoundError:
                    pass
            self.val[kw.FILENAME] = filename
            return None
Exemple #8
0
    def _parse_alphastart_vss(self, NAME, vss_str, variables, to_be_continued,
                              is_appending):
        """
        Parse the string vss_str with a start_vss/alpha_vss specification.

        Example: "S1->S2: 1.23, S2->S1:3.45, default:1" sets the parameter to
                 {('S1','S2'):1.23, ('S2','S1'):3.45, ('S1','S1'):1, ('S2','S2'):1}
                 under the assumption that stimulus_elements = {'S1', 'S2'}
        """
        if not self.val[kw.STIMULUS_ELEMENTS]:
            return f"The parameter 'stimulus_elements' must be assigned before the parameter '{NAME}'."

        # Create and populate the struct with None values
        if not is_appending:
            self.val[NAME] = dict()
            for e1 in self.val[kw.STIMULUS_ELEMENTS]:
                for e2 in self.val[kw.STIMULUS_ELEMENTS]:
                    self.val[NAME][(e1, e2)] = None
            self.val[NAME][kw.DEFAULT] = None

        single_vss, _ = ParseUtil.evaluate(vss_str, variables)
        if single_vss is not None:
            if is_appending:
                return f"A single value for '{NAME}' cannot follow other values."
            elif to_be_continued:
                return f"A single value for '{NAME}' cannot be followed by other values."
            else:
                for key in self.val[NAME]:
                    self.val[NAME][key] = single_vss
                self.val[NAME].pop(kw.DEFAULT)
        else:
            vs = ParseUtil.comma_split(vss_str)
            vs = [x.strip() for x in vs]
            for ee_str in vs:  # eb_v_str is 'e1->e2:value' or 'default:value'
                if ee_str.count(':') != 1:
                    return f"Expected 'x->y:value' or 'default:value' in '{NAME}', got '{ee_str}'."
                ee, v_str = ee_str.split(':')
                ee = ee.strip()
                v_str = v_str.strip()
                v, err = ParseUtil.evaluate(v_str, variables)
                if err:
                    return f"Invalid value '{v_str}' for '{ee}' in parameter '{NAME}'."

                if ee == kw.DEFAULT:
                    if self.val[NAME][kw.DEFAULT] is not None:
                        return f"Default value for '{NAME}' can only be stated once."
                    self.val[NAME][kw.DEFAULT] = v
                elif ee.count('->') == 1:
                    e1, e2 = ee.split('->')
                    if e1 not in self.val[kw.STIMULUS_ELEMENTS]:
                        return f"Error in parameter '{NAME}': '{e1}' is an invalid stimulus element."
                    if e2 not in self.val[kw.STIMULUS_ELEMENTS]:
                        return f"Error in parameter '{NAME}': '{e2}' is an invalid stimulus element."
                    if self.val[NAME][(e1, e2)] is not None:
                        return f"Duplicate of {e1}->{e2} in '{NAME}'."
                    self.val[NAME][(e1, e2)] = v
                else:
                    return f"Invalid string '{ee}' in parameter '{NAME}'."

            if not to_be_continued:
                # Set the default value for non-set stimulus-stimulus pairs
                err = self._set_default_values(NAME)
                if err:
                    return err

        return None  # No error
Exemple #9
0
    def _parse_stimulus_response_values(self, NAME, sr_str, variables,
                                        to_be_continued, is_appending):
        """
        Parse the string sr_str with a value for stimulus-response pairs.

        Example: "S1->R1: 1.23, S2->R1:3.45, default:1" sets the parameter to
                 {('S1','R1'):1.23, ('S1','R2'):1, ('S2','R1'):3.45, ('S2','R2'):1}
                 under the assumption that
                 behaviors = {'R1', 'R2'} and
                 stimulus_elements = {'S1', 'S2'}
        """
        if not self.val[kw.STIMULUS_ELEMENTS]:
            return f"The parameter 'stimulus_elements' must be assigned before the parameter '{NAME}'."
        if not self.val[kw.BEHAVIORS]:
            return f"The parameter 'behaviors' must be assigned before the parameter '{NAME}'."

        # Create and populate the struct with None values
        if not is_appending:
            self.val[NAME] = dict()
            for e in self.val[kw.STIMULUS_ELEMENTS]:
                for b in self.val[kw.BEHAVIORS]:
                    self.val[NAME][(e, b)] = None
            self.val[NAME][kw.DEFAULT] = None

        single_v, _ = ParseUtil.evaluate(sr_str, variables)
        if single_v is not None:
            if is_appending:
                return f"A single value for '{NAME}' cannot follow other values."
            elif to_be_continued:
                return f"A single value for '{NAME}' cannot be followed by other values."
            else:
                for key in self.val[NAME]:
                    self.val[NAME][key] = single_v
                self.val[NAME].pop(kw.DEFAULT)
        else:
            vs = ParseUtil.comma_split(sr_str)
            vs = [x.strip() for x in vs]
            for eb_v_str in vs:  # eb_v_str is 'e->b:value' or 'default:value'
                if eb_v_str.count(':') != 1:
                    return f"Expected 'x->y:value' or 'default:value' in '{NAME}', got '{eb_v_str}'."
                eb, v_str = eb_v_str.split(':')
                eb = eb.strip()
                v_str = v_str.strip()
                v, err = ParseUtil.evaluate(v_str, variables)
                if err:
                    return f"Invalid value '{v_str}' for '{eb}' in parameter '{NAME}'."

                if eb == kw.DEFAULT:
                    if self.val[NAME][kw.DEFAULT] is not None:
                        return f"Default value for '{NAME}' can only be stated once."
                    self.val[NAME][kw.DEFAULT] = v
                elif eb.count('->') == 1:
                    e, b = eb.split('->')
                    if e not in self.val[kw.STIMULUS_ELEMENTS]:
                        return f"Error in parameter '{NAME}': '{e}' is an invalid stimulus element."
                    if b not in self.val[kw.BEHAVIORS]:
                        return f"Error in parameter '{NAME}': '{b}' is an invalid behavior name."
                    if self.val[NAME][(e, b)] is not None:
                        return f"Duplicate of {e}->{b} in '{NAME}'."
                    self.val[NAME][(e, b)] = v
                else:
                    return f"Invalid string '{eb}' in parameter '{NAME}'."

            if not to_be_continued:
                # Set the default value for non-set stimulus-behavior pairs
                err = self._set_default_values(NAME)
                if err:
                    return err

        return None  # No error
Exemple #10
0
 def _is_valid_value(self, value_str):
     return ParseUtil.evaluate(value_str, self)
Exemple #11
0
    def next_stimulus(self, response, ignore_response_increment=False, preceeding_help_lines=None, omit_learn=None):
        # if not self.is_parsed:
        #     raise Exception("Internal error: Cannot call Phase.next_stimulus" +
        #                     " before Phase.parse().")

        if preceeding_help_lines is None:
            preceeding_help_lines = list()

        if not ignore_response_increment:
            # if not self.is_first_line:
            if response is not None:
                self.event_counter.increment_count(response)
                self.event_counter.increment_count_line(response)
                self.event_counter.set_last_response(response)

        if self.first_stimulus_presented:
            variables_both = Variables.join(self.global_variables, self.local_variables)
            if self.stop_condition.is_met(variables_both, self.event_counter):
                return None, None, preceeding_help_lines, None

        if self.is_first_line:
            # assert(response is None)
            rowlbl = self.first_label
            self.is_first_line = False
            omit_learn = True  # Since response is None, there is no learning (updating of v-values) to do
        else:
            rowlbl, omit_learn = self.curr_lineobj.next_line(response, self.global_variables, self.local_variables,
                                                             self.event_counter)
            self.prev_linelabel = self.curr_lineobj.label
            self._make_current_line(rowlbl)

        stimulus = self.phase_lines[rowlbl].stimulus
        if stimulus is not None:
            for element, intensity in stimulus.items():
                if type(intensity) is str:  # element[var] where var is a (local) variable
                    variables_both = Variables.join(self.global_variables, self.local_variables)
                    stimulus[element], err = ParseUtil.evaluate(intensity, variables=variables_both)
                    if err:
                        raise ParseException(self.phase_lines[rowlbl].lineno, err)

        if rowlbl != self.prev_linelabel:
            self.event_counter.reset_count_line()
            self.event_counter.line_label = rowlbl

        self.event_counter.increment_count(rowlbl)
        self.event_counter.increment_count_line(rowlbl)

        if stimulus is None:  # Help line
            action = self.phase_lines[rowlbl].action
            lineno = self.phase_lines[rowlbl].lineno
            self._perform_action(lineno, action)
            preceeding_help_lines.append(rowlbl)
            next_stimulus_out = self.next_stimulus(response, ignore_response_increment=True,
                                                   preceeding_help_lines=preceeding_help_lines,
                                                   omit_learn=omit_learn)
            stimulus, rowlbl, preceeding_help_lines, omit_learn_help = next_stimulus_out

            omit_learn = (omit_learn or omit_learn_help)
        else:
            for stimulus_element in stimulus:
                self.event_counter.increment_count(stimulus_element)
                self.event_counter.increment_count_line(stimulus_element)
            self.first_stimulus_presented = True

        return stimulus, rowlbl, preceeding_help_lines, omit_learn