def test_default_value(self):
     builder = CategoricalTableBuilder("Burglary")
     builder.add_row(ValueFactory.create(False), 0.8)
     assert builder.build().get_prob(ValueFactory.none()) == pytest.approx(
         0.199, abs=0.01)
     builder.remove_row(ValueFactory.create(False))
     assert builder.build().get_prob(ValueFactory.none()) == pytest.approx(
         0.999, abs=0.01)
     # assert node.hasProb(Assignment(), ValueFactory.none())
     builder = CategoricalTableBuilder("Burglary")
     builder.add_row(ValueFactory.create(False), 0.999)
     assert builder.build().get_prob(ValueFactory.none()) == pytest.approx(
         0.0, abs=0.01)
Example #2
0
    def get_prob_distrib(self, condition):
        """
        Fills the cache with the resulting table for the given condition

        :param condition: the condition for which to fill the cache
        """
        builder = CategoricalTableBuilder(self._base_var + self._primes)

        full_effects = list()
        for inputVal in condition.get_values():
            if isinstance(inputVal, Effect):
                full_effects.extend(inputVal.get_sub_effects())

        full_effect = Effect(full_effects)
        values = full_effect.get_values(self._base_var)
        if full_effect.is_non_exclusive(self._base_var):
            add_val = ValueFactory.create(list(values.keys()))
            builder.add_row(add_val, 1.0)
        elif len(values) > 0:
            total = 0.0
            for f in values.values():
                total += float(f)
            for v in values.keys():
                builder.add_row(v, values[v] / total)
        else:
            builder.add_row(ValueFactory.none(), 1.0)

        return builder.build()
    def __init__(self, arg1=None, arg2=None, arg3=1, arg4=True, arg5=False):
        if isinstance(arg1, Template) and isinstance(
                arg2, Template) and isinstance(arg3, int) and isinstance(
                    arg4, bool) and isinstance(arg5, bool):
            variable, value, priority, exclusive, negated = arg1, arg2, arg3, arg4, arg5
            """
            Constructs a new effect, with a variable label, value, and other arguments.
            The argument "add" specifies whether the effect is mutually exclusive with
            other effects. The argument "negated" specifies whether the effect includes
            a negation.
    
            :param variable: variable label
            :param value: variable value
            :param priority:the priority level (default is 1)
            :param exclusive: whether distinct values are mutually exclusive or not
            :param negated: whether to negate the effect or not.
            """
            super(TemplateEffect, self).__init__(
                str(variable),
                ValueFactory.none() if value.is_under_specified() else
                ValueFactory.create(str(value)), priority, exclusive, negated)

            self._label_template = variable
            self._value_template = value

        else:
            raise NotImplementedError()
Example #4
0
    def fill_slots(self, assignment):
        """
        Fills the slots of the template, and returns the result of the function
        evaluation. If the function is not a simple arithmetic expression,
        """
        filled = super(ArithmeticTemplate, self).fill_slots(assignment)
        if '{' in filled:
            return filled

        if ArithmeticTemplate.is_arithmetic_expression(filled):
            try:
                return StringUtils.get_short_form(
                    MathExpression(filled).evaluate())
            # TODO: need to check exception handling
            except Exception as e:
                self.log.warning("cannot evaluate " + filled)
                return filled

        # handling expressions that manipulate sets
        # (using + and - to respectively add/remove elements)
        merge = ValueFactory.none()
        for str_val in filled.split("+"):
            negations = str_val.split("-")
            merge = merge.concatenate(ValueFactory.create(negations[0]))
            for negation in negations[1:]:
                values = merge.get_sub_values()

                old_value = ValueFactory.create(negation)
                if old_value in values:
                    values.remove(ValueFactory.create(negation))

                merge = ValueFactory.create(values)

        return str(merge)
Example #5
0
    def remove_from_state(self, variable_id):
        """
        Removes the variable from the dialogue state

        :param variable_id: the node to remove
        """
        with self._locks['remove_from_state']:
            self.add_to_state(Assignment(variable_id, ValueFactory.none()))
Example #6
0
    def get_value(self, variable):
        """
        Returns the value associated with the variable in the assignment, if one is
        specified. Else, returns the none value.

        :param variable: the variable
        :return: the associated value
        """
        return self._map.get(variable, ValueFactory.none())
Example #7
0
    def start(self):
        """
        Adds an empty action to the dialogue system to start the interaction.
        """
        empty_action = Assignment(self.system.get_settings().system_output, ValueFactory.none())

        if self.system.is_paused():
            self.system.get_state().add_to_state(empty_action)
        else:
            self.system.add_content(empty_action)
        self.system.attach_module(RewardLearner)
Example #8
0
    def is_filled_by(self, assignment):
        """
        Returns true if all slots are filled by the assignment. Else, returns false.
        """
        # TODO: check whether this function has a bug or not.
        for slot_key in self._slots:
            value = assignment.get_value(slot_key)
            if value == ValueFactory.none():
                return False

        return True
    def is_empty(self):
        """
        Returns true if the table is empty (or contains only a default assignment),
        false otherwise

        :return: true if empty, false otherwise
        """
        if len(self._table) == 0:
            return True

        return len(self._table) == 1 and list(
            self._table.keys())[0] == ValueFactory.none()
Example #10
0
    def is_default(self):
        """
        Returns true if the assignment only contains none values for all variables,
        and false if at least one has a different value.

        :return: true if all variables have none values, false otherwise
        """
        for variable in self._map.keys():
            if self._map[variable] != ValueFactory.none():
                return False

        return True
    def get_prob_distrib(self, condition):
        """
        Returns the (unconditional) probability distribution P(X) given the conditional assignment.

        :param condition: the conditional assignment
        :return: the corresponding probability distribution
        """
        if condition in self._table:
            return self._table[condition]
        else:
            # TODO: check refactor > raise exception?
            return SingleValueDistribution(self._head_var, ValueFactory.none())
Example #12
0
    def get_values(self, variables):
        """
        Returns the list of values corresponding to a subset of variables in the
        assignment (in the same order)

        :param variables: the subset of variable labels
        :return: the corresponding values
        """
        values = []
        for variable in variables:
            values.append(self._map.get(variable, ValueFactory.none()))

        return values
Example #13
0
    def create_default(variables):
        """
        Creates an assignment with only none values for the variable labels given as argument.

        :param args:
        :param variables: the collection of variable labels
        :return: the resulting default assignment
        """
        assignment = Assignment()
        for variable in variables:
            assignment.add_pair(variable, ValueFactory.none())

        return assignment
    def get_prob(self, condition):
        """
        Returns the probability of eq=true given the condition

        :param condition: the conditional assignment
        :return: the probability of eq=true
        """
        predicted = None
        actual = None
        for input_var in condition.get_variables():
            if input_var == self._base_var + "^p":
                predicted = condition.get_value(input_var)
            elif input_var == self._base_var + "'":
                actual = condition.get_value(input_var)
            elif input_var == self._base_var:
                actual = condition.get_value(input_var)

        if predicted is None or actual is None:
            raise ValueError()

        if predicted == ValueFactory.none() or actual == ValueFactory.none():
            return EquivalenceDistribution.none_prob
        elif predicted == actual:
            return 1.0
        elif isinstance(predicted, StringVal) and isinstance(actual, StringVal):
            str1 = str(predicted)
            str2 = str(actual)
            if Template.create(str1).match(str2).is_matching() or Template.create(str2).match(str1).is_matching():
                return 1.0
            return 0.0
        elif len(predicted.get_sub_values()) > 0 and len(actual.get_sub_values()) > 0:
            vals0 = predicted.get_sub_values()
            vals1 = actual.get_sub_values()
            intersect = set(vals0)
            intersect.intersection_update(vals1)

            return float(len(intersect) / len(vals0))
        else:
            return 0.0
Example #15
0
    def get_values(self):
        """
        Returns the possible outputs values given the input range in the parent nodes
        (probability rule nodes)

        :return: the possible values for the output
        """
        values = set()

        for rule in self._input_rules:
            for effect in rule.get_effects():
                if effect.is_non_exclusive(self._base_var):
                    return self.get_values_linearise()
                set_values = set(effect.get_values(self._base_var).keys())
                if set_values:
                    values.update(set_values)
                else:
                    values.add(ValueFactory.none())

        if len(values) == 0:
            values.add(ValueFactory.none())

        return values
Example #16
0
    def get_nodes_to_keep(state):
        """
        Selects the set of variables to retain in the dialogue state.

        :param state: the dialogue state
        :return: the set of variable labels to keep
        """
        nodes_to_keep = set()

        for chance_node in state.get_chance_nodes():
            if chance_node.get_id()[:2] == "=_" or chance_node.get_id(
            )[-2:] == "^t" or chance_node.get_id()[-2:] == "^o":
                continue
            elif StatePruner.enable_reduction and isinstance(
                    chance_node.get_distrib(), AnchoredRule):
                continue
            elif len(chance_node.get_input_node_ids()
                     ) < 3 and chance_node.get_nb_values() == 1 and list(
                         chance_node.get_values())[0] == ValueFactory.none():
                continue
            elif chance_node.get_id()[-2:] == "^p":
                flag = False
                for node_ids in chance_node.get_output_node_ids():
                    if node_ids[:2] == "=_":
                        flag = True
                        break
                if flag:
                    continue

            if not state.has_chance_node(chance_node.get_id() + "'"):
                nodes_to_keep.add(chance_node.get_id())

            if state.is_incremental(chance_node.get_id()):
                for descendant_id in chance_node.get_descendant_ids():
                    if not state.has_chance_node(descendant_id):
                        continue
                    if state.has_chance_node(descendant_id + "'"):
                        continue
                    nodes_to_keep.add(descendant_id)

            if chance_node.get_id() in state.get_parameter_ids(
            ) and not chance_node.has_descendant(
                    state.get_evidence().get_variables()):
                for output_node in chance_node.get_output_nodes(ChanceNode):
                    if not isinstance(output_node.get_distrib(), AnchoredRule):
                        continue
                    nodes_to_keep.add(output_node.get_id())

        return nodes_to_keep
Example #17
0
    def get_assignment(self):
        """
        Returns the effect as an assignment of values. The variable labels are ended
        by a prime character.

        :return: the assignment of new values to the variables
        """
        assignment = Assignment()
        for effect in self._sub_effects:
            if not effect._negated:
                assignment.add_pair(effect.get_variable() + "'",
                                    effect.get_value())
            else:
                assignment.add_pair(effect.get_variable() + "'",
                                    ValueFactory.none())
        return assignment
    def sample(self, condition):
        """
        Sample a head assignment from the distribution P(head|condition), given the
        condition. If no assignment can be sampled (due to e.g. an ill-formed
        distribution), returns an empty assignment.

        :param condition: the condition
        :return: the sampled assignment the condition
        """
        if condition.size() != len(self._conditional_vars):
            condition = condition.get_trimmed(self._conditional_vars)

        subdistrib = self._table[condition]
        if subdistrib is not None:
            return subdistrib.sample()

        # TODO: check refactor > raise exception?
        return ValueFactory.none()
Example #19
0
    def perform_turn(self):
        system_state = self.system.get_state()
        output_var = self.system.get_settings().system_output

        try:
            system_action = ValueFactory.none()
            if system_state.has_chance_node(output_var):
                system_action = system_state.query_prob(output_var).get_best()
            self.log.debug("Simulator input: %s" % system_action)

            turn_performed = self.perform_turn(system_action)
            repeat = 0
            while not turn_performed and repeat < 5 and self.system.get_modules().contains(self):
                turn_performed = self.perform_turn(system_action)
                repeat += 1

        except Exception as e:
            self.log.debug("cannot update simulator: " + str(e))
Example #20
0
 def test_classical(self):
     assert isinstance(ValueFactory.create(' blabla '), StringVal)
     assert isinstance(ValueFactory.create('3'), DoubleVal)
     assert isinstance(ValueFactory.create('3.6'), DoubleVal)
     assert ValueFactory.create('3').get_double() == pytest.approx(
         3.0, abs=0.0001)
     assert isinstance(ValueFactory.create('[firstItem, secondItem, 3.6]'),
                       SetVal)
     assert len(
         ValueFactory.create(
             '[firstItem, secondItem, 3.6]').get_sub_values()) == 3
     assert ValueFactory.create(
         '[firstItem, secondItem, 3.6]').get_sub_values() == {
             ValueFactory.create('firstItem'),
             ValueFactory.create('secondItem'),
             ValueFactory.create(3.6)
         }
     assert isinstance(ValueFactory.create('[0.6, 0.4, 32]'), ArrayVal)
     assert len(ValueFactory.create('[0.6, 0.4, 32]').get_array()) == 3
     assert ValueFactory.create(
         '[0.6, 0.4, 32]').get_array()[2] == pytest.approx(32, abs=0.0001)
     assert isinstance(ValueFactory.create('True'), BooleanVal)
     assert not ValueFactory.create('False').get_boolean()
     assert ValueFactory.create('None') == ValueFactory.none()
     assert not ValueFactory.create('firsttest').__lt__(
         ValueFactory.create('firsttest'))
     assert ValueFactory.create('firsttest').__lt__(
         ValueFactory.create('secondTest'))
     assert ValueFactory.create(3.0).__lt__(ValueFactory.create(5.0))
     assert not ValueFactory.create(5.0).__lt__(ValueFactory.create(3.0))
     assert (ValueFactory.create(5.0).__lt__(
         ValueFactory.create('test'))) == (
             ValueFactory.create('test').__lt__(ValueFactory.create(5.0)))
     assert len(
         ValueFactory.create('[test,[1,2],True]').get_sub_values()) == 3
     assert ValueFactory.create('test') in ValueFactory.create(
         '[test,[1,2],True]').get_sub_values()
     assert ValueFactory.create('[1,2]') in ValueFactory.create(
         '[test,[1,2],True]').get_sub_values()
     assert ValueFactory.create('True') in ValueFactory.create(
         '[test,[1,2],True]').get_sub_values()
     assert len(
         ValueFactory.create(
             '[a1=test,a2=[1,2],a3=true]').get_sub_values()) == 3
    def sample(self):
        """
        Sample a value from the distribution. If no assignment can be sampled (due to
        e.g. an ill-formed distribution), returns a none value.

        :return: the sampled assignment
        """
        if self._intervals is None:
            if len(self._table) == 0:
                self.log.warning("creating intervals for an empty table")
                raise ValueError()

            self._intervals = Intervals(self._table)

        if self._intervals.is_empty():
            self.log.warning("interval is empty, table: ", self._table)
            return ValueFactory.none()

        sample = self._intervals.sample()
        return sample
    def generate_xml(self):
        """
        Generates the XML representation for the table, for the document doc.

        :param doc: the XML document for which to generate the XML.
        :return: XML reprensetation
        """
        var = Element("variable")
        var.set("id", self._variable.replace("'", ""))
        for v in InferenceUtils.get_n_best(self._table,
                                           len(self._table)).keys():
            if v != ValueFactory.none():
                value_node = Element("value")
                if self._table[v] < 0.99:
                    value_node.set("prob",
                                   StringUtils.get_short_form(self._table[v]))
                value_node.text = str(v)
                var.append(value_node)

        return var
Example #23
0
    def get_values_linearise(self):
        """
        Calculates the possible values for the output distribution via linearisation
        (more costly operation, but necessary in case of add effects).

        :return: the set of possible output values
        """
        table = dict()
        for i in range(len(self._input_rules)):
            table[str(i)] = self._input_rules[i].get_effects()

        combinations = InferenceUtils.get_all_combinations(table)

        values = set()
        for cond in combinations:
            values.update(self.get_prob_distrib(cond).get_values())

        if len(values) == 0:
            values.add(ValueFactory.none())

        return values
Example #24
0
    def create_table(self, variable):
        """
        Extracts the values (along with their weight) specified in the effect.

        :param variable: variable the variable
        :return: the corresponding values
        """
        values = dict()

        max_priority = sys.maxsize
        to_remove = set()
        to_remove.add(ValueFactory.none())

        for effect in self._sub_effects:
            if effect.get_variable() == variable:
                if effect._priority < max_priority:
                    max_priority = effect._priority
                if effect._negated:
                    to_remove.add(effect.get_value())

        for effect in self._sub_effects:
            effect_variable = effect.get_variable()
            if effect_variable == variable:
                effect_value = effect.get_value()
                if effect._priority > max_priority or effect._negated or effect_value in to_remove:
                    continue
                if len(to_remove) > 1 and isinstance(effect_value, SetVal):
                    sub_values = effect_value.get_sub_values()
                    for r in to_remove:
                        if r in sub_values:
                            sub_values.remove(r)

                    effect_value = ValueFactory.create(sub_values)

                if effect_value not in values:
                    values[effect_value] = effect._weight
                else:
                    values[effect_value] += effect._weight

        return values
Example #25
0
    def remove_spurious_nodes(reduced):
        """
        Removes all non-necessary nodes from the dialogue state.

        :param reduced: the reduced dialogue state
        """
        for chance_node in set(reduced.get_chance_nodes()):
            if len(chance_node.get_input_nodes()) == 0 and len(
                    chance_node.get_output_nodes()) == 0 and isinstance(
                        chance_node.get_distrib(),
                        CategoricalTable) and chance_node.get_prob(
                            ValueFactory.none()) > 0.99:
                reduced.remove_node(chance_node.get_id())
                continue

            if isinstance(chance_node.get_distrib(),
                          EquivalenceDistribution) and len(
                              chance_node.get_input_node_ids()) == 0:
                reduced.remove_node(chance_node.get_id())

            chance_node.prune_values(StatePruner.value_pruning_threshold)

            if chance_node.get_nb_values() == 1 and len(
                    chance_node.get_output_nodes()) > 0 and len(
                        reduced.get_incremental_vars()) == 0:
                assignment = Assignment(chance_node.get_id(),
                                        chance_node.sample())
                for output_node in chance_node.get_output_nodes(ChanceNode):
                    if not isinstance(output_node.get_distrib(), AnchoredRule):
                        cur_distrib = output_node.get_distrib()
                        output_node.remove_input_node(chance_node.get_id())
                        if len(output_node.get_input_node_ids()) == 0:
                            output_node.set_distrib(
                                cur_distrib.get_prob_distrib(assignment))
                        else:
                            output_node.set_distrib(
                                cur_distrib.get_posterior(assignment))
Example #26
0
    def get_groundings(self, param):
        """
        Returns the set of possible groundings for the given input assignment

        :param param: the input assignment
        :return: the set of possible (alternative) groundings for the condition
        """
        ground_cond = BasicCondition(self, param)
        groundings = RuleGrounding()

        if len(ground_cond._variable.get_slots()) > 0:
            for inputVar in param.get_variables():
                m = ground_cond._variable.match(inputVar)
                if m.is_matching():
                    new_input = Assignment([param, m])
                    spec_grounds = self.get_groundings(new_input)
                    spec_grounds.extend(m)
                    groundings.add(spec_grounds)
            return groundings

        filled_var = str(ground_cond._variable)
        if len(ground_cond._template_value.get_slots()) > 0:
            actual_value = param.get_value(str(ground_cond._variable))
            groundings = ground_cond.get_groundings(actual_value)
            groundings.remove_variables(param.get_variables())
            groundings.remove_value(ValueFactory.none())

        elif self._relation == Relation.IN and not param.contains_var(
                filled_var):
            values_coll = ground_cond._ground_value.get_sub_values()
            groundings.extend(filled_var, values_coll)

        elif not self.is_satisfied_by(param):
            groundings.set_as_failed()

        return groundings
Example #27
0
    def __init__(self, arg1=None, arg2=None):
        if isinstance(arg1, str) and arg2 is None:
            node_id = arg1
            """
            Creates a new action node with a unique identifier, and no values

            :param node_id: the node identifier
            """
            super(ActionNode, self).__init__(node_id)
            self._action_values = set()
            self._action_values.add(ValueFactory.none())
        elif isinstance(arg1, str) and isinstance(arg2, set):
            node_id = arg1
            action_values = arg2
            """
            Creates a new action node with a unique identifier and a set of values
            :param node_id: the node identifier
            :param action_values: the values for the action
            """
            super(ActionNode, self).__init__(node_id)
            self._action_values = set()
            self._action_values.update(action_values)
        else:
            raise NotImplementedError("UNDEFINED PARAMETERS")
Example #28
0
    def fill_slots(self, fillers):
        """
        Fills the template with the given content, and returns the filled string. The
        content provided in the form of a slot:filler mapping. For instance, given a
        template: "my name is {name}" and a filler "name:Pierre", the method will
        return "my name is Pierre".

        :param fillers: the content associated with each slot.
        :return: the string filled with the given content
        """
        if len(self._slots) == 0:
            return self._str_val

        result = self._str_val
        for slot_key in self._slots.keys():
            value = fillers.get_value(slot_key)
            if isinstance(value, CustomVal):
                assert("{%s}" % slot_key == self._str_val)  # only {custom_value} is allowed.
                result = value.get_value()
            elif value != ValueFactory.none():
                str_val = str(value)
                result = result.replace("{%s}" % slot_key, str_val)

        return result