示例#1
0
    def __init__(self, arg1=None, arg2=None):
        if isinstance(arg1, str) and isinstance(arg2, ProbDistribution):
            node_id = arg1
            distrib = arg2
            """
            Creates a new chance node, with the given identifier and probability distribution

            :param node_id: the unique node identifier
            :param distrib: the probability distribution for the node
            """
            super(ChanceNode, self).__init__(node_id)
            if distrib.get_variable() != node_id:
                self.log.warning(node_id + "  != " + distrib.get_variable())
            self._distrib = distrib
            self._cached_values = None
        elif isinstance(arg1, str) and isinstance(arg2, Value):
            node_id = arg1
            value = arg2
            """
            Creates a change node with a unique value (associated with a probability 1.0)

            :param node_id: the unique node identifier
            :param value: the single value for the node
            """
            super(ChanceNode, self).__init__(node_id)
            self._distrib = SingleValueDistribution(node_id, value)
            self._cached_values = None
        else:
            raise NotImplementedError("UNDEFINED PARAMETERS")
示例#2
0
    def add_to_state(self, assignment):
        """
        Adds the content provided as argument to the dialogue state. If the state
        variables in the assignment already exist, they are erased.

        :param assignment: the value assignment to add
        """
        with self._locks['add_to_state_assignment']:
            for variable in assignment.get_variables():
                self.add_to_state(
                    SingleValueDistribution(variable,
                                            assignment.get_value(variable)))
    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())
    def test6(self):
        old = copy(TestNetworkReduction.network)

        TestNetworkReduction.network.get_node("Alarm").remove_input_node(
            "Earthquake")
        TestNetworkReduction.network.get_node("Alarm").remove_input_node(
            "Burglary")
        TestNetworkReduction.network.get_chance_node("Alarm").set_distrib(
            SingleValueDistribution("Alarm", "False"))

        self.test1()
        self.test2()
        self.test3()

        TestNetworkReduction.network = old
示例#5
0
    def test1(self):
        domain = XMLDomainReader.extract_domain(TestIncremental.domain_file)
        system = DialogueSystem(domain)

        # NEED GUI & Recording
        system.get_settings().show_gui = False
        # system.get_settings().recording = Settings.Recording.ALL

        system.start_system()
        system.add_content(system.get_settings().user_speech, "busy")
        system.add_incremental_content(SingleValueDistribution("u_u", "go"),
                                       False)

        sleep(0.1)

        assert ValueFactory.create("go") in system.get_content(
            "u_u").get_values()

        t = CategoricalTableBuilder("u_u")
        t.add_row("forward", 0.7)
        t.add_row("backward", 0.2)

        system.add_incremental_content(t.build(), True)

        sleep(0.1)

        assert ValueFactory.create("go forward") in system.get_content(
            "u_u").get_values()
        assert system.get_content("u_u").get_prob(
            "go backward") == pytest.approx(0.2, abs=0.001)
        assert system.get_state().has_chance_node("nlu")

        system.add_content(system.get_settings().user_speech, "None")
        assert len(system.get_state().get_chance_nodes()) == 7
        system.add_incremental_content(
            SingleValueDistribution("u_u", "please"), True)
        assert system.get_state().get_evidence().contains_pair(
            "=_a_u", ValueFactory.create(True))

        assert system.get_content("u_u").get_prob(
            "go please") == pytest.approx(0.1, abs=0.001)
        assert system.get_state().has_chance_node("nlu")

        system.get_state().set_as_committed("u_u")
        assert not system.get_state().has_chance_node("nlu")

        t2 = CategoricalTableBuilder("u_u")
        t2.add_row("I said go backward", 0.3)

        system.add_incremental_content(t2.build(), True)
        assert system.get_content("a_u").get_prob(
            "Request(Backward)") == pytest.approx(0.82, abs=0.05)
        assert ValueFactory.create("I said go backward") in system.get_content(
            "u_u").get_values()
        assert system.get_state().has_chance_node("nlu")

        system.get_state().set_as_committed("u_u")
        assert not system.get_state().has_chance_node("nlu")
        system.add_incremental_content(
            SingleValueDistribution("u_u", "yes that is right"), False)
        assert ValueFactory.create("yes that is right") in system.get_content(
            "u_u").get_values()
示例#6
0
class ChanceNode(BNode):
    """
    Representation of a chance node (sometimes also called belief node), which is a
    random variable associated with a specific probability distribution.
    """

    # logger
    log = logging.getLogger('PyOpenDial')

    # ===================================
    # NODE CONSTRUCTION
    # ===================================

    def __init__(self, arg1=None, arg2=None):
        if isinstance(arg1, str) and isinstance(arg2, ProbDistribution):
            node_id = arg1
            distrib = arg2
            """
            Creates a new chance node, with the given identifier and probability distribution

            :param node_id: the unique node identifier
            :param distrib: the probability distribution for the node
            """
            super(ChanceNode, self).__init__(node_id)
            if distrib.get_variable() != node_id:
                self.log.warning(node_id + "  != " + distrib.get_variable())
            self._distrib = distrib
            self._cached_values = None
        elif isinstance(arg1, str) and isinstance(arg2, Value):
            node_id = arg1
            value = arg2
            """
            Creates a change node with a unique value (associated with a probability 1.0)

            :param node_id: the unique node identifier
            :param value: the single value for the node
            """
            super(ChanceNode, self).__init__(node_id)
            self._distrib = SingleValueDistribution(node_id, value)
            self._cached_values = None
        else:
            raise NotImplementedError("UNDEFINED PARAMETERS")

    @dispatch(ProbDistribution)
    def set_distrib(self, distrib):
        """
        Sets the probability distribution of the node, and erases the existing one.

        :param distrib: the distribution for the node
        """
        self._distrib = distrib
        if distrib.get_variable() != self._node_id:
            self.log.warning(self._node_id + "  != " + distrib.get_variable())
            raise ValueError()

        self._cached_values = None

    @dispatch(BNode)
    def add_input_node(self, input_node):
        """
        Adds a new (input) relation for the node.

        :param input_node: the input node to connect
        """
        super(ChanceNode, self).add_input_node(input_node)

    @dispatch(str)
    def set_id(self, new_id):
        """
        Replaces the node identifier with a new one.

        :param new_id: the new identifier
        """
        old_id = self._node_id
        super(ChanceNode, self).set_id(new_id)
        self._distrib.modify_variable_id(old_id, new_id)

    @dispatch(float)
    def prune_values(self, threshold):
        """
        Prune the values with a probability below a given threshold.

        :param threshold: the probability threshold
        """
        if self._distrib.prune_values(threshold):
            self._cached_values = None

    # ===================================
    # GETTERS
    # ===================================

    @dispatch(Value)
    def get_prob(self, node_value):
        """
        Returns the probability associated with a specific value, according to the
        current distribution.

        The method assumes that the node is conditionally independent of every other
        node. If it isn't, one should use the getProb(condition, nodeValue) method
        instead.

        NB: the method should *not* be used to perform sophisticated inference, as it
        is not optimised and might lead to distorted results for very dependent
        networks

        :param node_value: the value for the node
        :return: its probability
        """
        if isinstance(self._distrib, IndependentDistribution):
            return self._distrib.get_prob(node_value)

        combinations = self.get_possible_conditions()
        total_prob = 0.
        for condition in combinations:
            prob = 1.
            for input_node in self._input_nodes:
                if isinstance(input_node, ChanceNode):
                    value = condition.get_value(input_node.get_id())
                    prob *= input_node.get_prob(value)

            total_prob += prob * self._distrib.get_prob(condition, node_value)

        return total_prob

    @dispatch(Assignment, Value)
    def get_prob(self, condition, node_value):
        """
        Returns the probability associated with the conditional assignment and the
        node value, if one is defined.

        :param condition: the condition
        :param node_value: the value for the node
        :return: the associated probability
        """
        try:
            return self._distrib.get_prob(condition, node_value)
        except Exception as e:
            self.log.warning("exception: %s" % e)
            raise ValueError()

    @dispatch()
    def sample(self):
        """
        Returns a sample value for the node, according to the probability distribution
        currently defined.

        The method assumes that the node is conditionally independent of every other
        node. If it isn't, one should use the sample(condition) method instead.

        :return: the sample value
        """
        if isinstance(self._distrib, IndependentDistribution):
            return self._distrib.sample()

        input_sample = Assignment()
        for input_node in self._input_nodes.values():
            if isinstance(input_node, ChanceNode) or isinstance(
                    input_node, ActionNode):
                input_sample.add_pair(input_node.get_id(), input_node.sample())

        return self.sample(input_sample)

    @dispatch(Assignment)
    def sample(self, condition):
        """
        Returns a sample value for the node, given a condition. The sample is selected
        according to the probability distribution for the node.

        :param condition: the value assignment on conditional nodes
        :return: the sample value
        """
        if isinstance(self._distrib, IndependentDistribution):
            return self._distrib.sample()
        else:
            return self._distrib.sample(condition)

    @dispatch()
    def get_values(self):
        """
        Returns a discrete set of values for the node. If the variable for the node
        has a continuous range, this set if based on a discretisation procedure
        defined by the distribution.

        :return: the discrete set of values
        """
        if self._cached_values is None:
            self._cached_values = self._distrib.get_values()

        return self._cached_values

    @dispatch()
    def get_nb_values(self):
        """
        Returns the number of values for the node.

        :return: the number of values
        """
        if isinstance(self._distrib, ContinuousDistribution):
            return Settings.discretization_buckets
        else:
            return len(self.get_values())

    @dispatch()
    def get_distrib(self):
        """
        Returns the probability distribution attached to the node.

        :return: the distribution
        """
        return self._distrib

    @dispatch()
    def get_factor(self):
        """
        Returns the "factor matrix" mapping assignments of conditional variables + the
        node variable to a probability value.

        :return: the factor matrix
        """
        factor = dict()
        conditions = self.get_possible_conditions()

        for condition in conditions:
            posterior = self._distrib.get_prob_distrib(condition)
            for value in posterior.get_values():
                factor[Assignment(condition, self._node_id,
                                  value)] = posterior.get_prob(value)

        return factor

    # ===================================
    # UTILITIES
    # ===================================

    def __copy__(self):
        """
        Returns a copy of the node. Note that only the node content is copied, not its
        connection with other nodes.

        :return: the copy
        """
        chance_node = ChanceNode(self._node_id, copy(self._distrib))
        if self._cached_values is not None:
            chance_node._cached_values = copy(self._cached_values)
        return chance_node

    def __hash__(self):
        """
        Returns the hashcode for the node (based on the hashcode of the identifier and
        the distribution).

        :return: the hashcode for the node
        """
        return super(ChanceNode, self).__hash__() + self._distrib.__hash__()

    def __str__(self):
        """
        Returns the string representation of the distribution.

        :return: the string representation for the distribution
        """
        return self._distrib.__str__()

    __repr__ = __str__

    # ===================================
    # PRIVATE AND PROTECTED METHODS
    # ===================================

    @dispatch(str, str)
    def modify_variable_id(self, old_id, new_id):
        """
        Modify the variable identifier with new identifier.

        :param old_id: the old identifier for the node
        :param new_id: the new identifier for the node
        """
        super(ChanceNode, self).modify_variable_id(old_id, new_id)
        self._distrib.modify_variable_id(old_id, new_id)
示例#7
0
    def test_demo(self):
        domain = XMLDomainReader.extract_domain(TestDemo.domain_file2)
        system = DialogueSystem(domain)
        system.get_settings().show_gui = False

        system.start_system()
        assert len(system.get_state().get_chance_nodes()) == 5

        t = CategoricalTableBuilder("u_u")
        t.add_row("hello there", 0.7)
        t.add_row("hello", 0.2)
        updates = system.add_content(t.build())
        assert updates.issuperset({"a_u", "a_m", "u_m"})

        assert str(system.get_content("u_m").get_best()) == "Hi there"

        t2 = dict()
        t2["move forward"] = 0.06
        system.add_user_input(t2)

        assert not system.get_state().has_chance_node("u_m")

        t2 = dict()
        t2["move forward"] = 0.45
        system.add_user_input(t2)

        assert str(
            system.get_content("u_m").get_best()) == "OK, moving Forward"

        t = CategoricalTableBuilder("u_u")
        t.add_row("now do that again", 0.3)
        t.add_row("move backward", 0.22)
        t.add_row("move a bit to the left", 0.22)
        system.add_content(t.build())

        assert str(
            system.get_content("u_m").get_best()) == "Sorry, could you repeat?"

        t = CategoricalTableBuilder("u_u")
        t.add_row("do that one more time", 0.65)
        system.add_content(t.build())

        assert str(
            system.get_content("u_m").get_best()) == "OK, moving Forward"

        system.add_content(SingleValueDistribution("perceived", "[BlueObj]"))

        t = CategoricalTableBuilder("u_u")
        t.add_row("what do you see", 0.6)
        t.add_row("do you see it", 0.3)
        system.add_content(t.build())
        assert str(
            system.get_content("u_m").get_best()) == "I see a blue cylinder"

        t = CategoricalTableBuilder("u_u")
        t.add_row("pick up the blue object", 0.75)
        t.add_row("turn left", 0.12)
        system.add_content(t.build())

        assert str(system.get_content(
            "u_m").get_best()) == "OK, picking up the blue object"

        system.add_content(SingleValueDistribution("perceived", "[]"))
        system.add_content(SingleValueDistribution("carried", "[BlueObj]"))

        t = CategoricalTableBuilder("u_u")
        t.add_row("now please move a bit forward", 0.21)
        t.add_row("move backward a little bit", 0.13)
        system.add_content(t.build())

        assert str(system.get_content(
            "u_m").get_best()) == "Should I move a bit forward?"

        t = CategoricalTableBuilder("u_u")
        t.add_row("yes", 0.8)
        t.add_row("move backward", 0.1)
        system.add_content(t.build())

        assert str(system.get_content(
            "u_m").get_best()) == "OK, moving Forward a little bit"

        t = CategoricalTableBuilder("u_u")
        t.add_row("and now move forward", 0.21)
        t.add_row("move backward", 0.09)
        system.add_content(t.build())

        assert str(
            system.get_content("u_m").get_best()) == "Should I move forward?"

        t = CategoricalTableBuilder("u_u")
        t.add_row("no", 0.6)
        system.add_content(t.build())

        assert str(
            system.get_content("u_m").get_best()) == "Should I move backward?"

        t = CategoricalTableBuilder("u_u")
        t.add_row("yes", 0.5)
        system.add_content(t.build())

        assert str(
            system.get_content("u_m").get_best()) == "OK, moving Backward"

        t = CategoricalTableBuilder("u_u")
        t.add_row("now what can you see now?", 0.7)
        system.add_content(t.build())

        assert str(
            system.get_content("u_m").get_best()) == "I do not see anything"

        t = CategoricalTableBuilder("u_u")
        t.add_row("please release the object", 0.5)
        system.add_content(t.build())

        assert str(system.get_content(
            "u_m").get_best()) == "OK, putting down the object"

        t = CategoricalTableBuilder("u_u")
        t.add_row("something unexpected", 0.7)
        system.add_content(t.build())

        assert not system.get_state().has_chance_node("u_m")

        t = CategoricalTableBuilder("u_u")
        t.add_row("goodbye", 0.7)
        system.add_content(t.build())

        assert str(
            system.get_content("u_m").get_best()) == "Bye, see you next time"