Beispiel #1
0
    def _alter_constraints(self, constraints, count):
        """
        Alters *count* constraints from the given constraints by choosing a new value
        (could be also 'dontcare').
        """
        constraints_candidates = constraints[:]  # copy list
        if not constraints_candidates:
            for _constraint in self.goal.constraints:
                if _constraint.value != 'dontcare':
                    constraints_candidates.append(
                        Constraint(_constraint.slot, _constraint.value))
        else:
            # any constraint from the current system actions has to be taken into consideration
            # make sure that constraints are part of the goal since noise could have influenced the
            # dialog -> given constraints must conform to the current goal
            constraints_candidates = list(filter(lambda x:\
                not self.goal.is_inconsistent_constraint_strict(x), constraints_candidates))

        if not constraints_candidates:
            return []

        constraints_to_alter = common.numpy.random.choice(
            constraints_candidates, count, replace=False)

        new_constraints = []
        for _constraint in constraints_to_alter:
            self.goal.excluded_inf_slot_values[_constraint.slot].add(
                _constraint.value)
            possible_values = self.goal.inf_slot_values[_constraint.slot][:]
            for _value in self.goal.excluded_inf_slot_values[_constraint.slot]:
                # remove values which have been tried already
                # NOTE values in self.excluded_inf_slot_values should always be in possible_values
                # because the same source is used for both and to initialize the goal
                possible_values.remove(_value)

            if not possible_values:
                # add 'dontcare' as last option
                possible_values.append('dontcare')

            # 'dontcare' value with some probability
            if common.random.random(
            ) < self.parameters['usermodel']['DontcareIfNoVenue']:
                value = 'dontcare'
            else:
                value = common.numpy.random.choice(possible_values)
            if not self.goal.update_constraint(_constraint.slot, value):
                # NOTE: this case should never happen!
                print(
                    "The given constraints (probably by the system) are not part of the goal!"
                )
            new_constraints.append(Constraint(_constraint.slot, value))

        self.logger.dialog_turn("Goal altered! {} -> {}.".format(
            constraints_to_alter, new_constraints))

        return new_constraints
Beispiel #2
0
 def add_constraint(self, entries):
     entries = [int(e) for e in entries]
     if self.type == "T":
         c = Constraint(self.type,
                        day=entries[0],
                        time_slot=entries[1],
                        teacher=self.id)
     else:
         c = Constraint(self.type, tc_avail=entries)
     self.CI.append(c)
     for ba in self.bas:
         ba.add_constraint(copy.deepcopy(c))
Beispiel #3
0
    def clean(self, goal: Goal):
        """Cleans the agenda, i.e. makes sure that actions are consistent with goal and in the
        correct order.

        Args:
            goal: The goal which is needed to determine the consistent actions.

        """
        cleaned_stack = []
        # reverse order since most recent actions are on top of agenda
        for action in self.stack[::-1]:
            if action not in cleaned_stack:
                # NOTE sufficient if there is only one slot per (request) action
                # remove accomplished requests
                if (action.type is not UserActionType.Request
                        or (action.slot in goal.requests
                            and goal.requests[action.slot] is None)
                        or not action.slot in goal.requests):
                    # make sure to remove "old" inform actions
                    if action.type is UserActionType.Inform:
                        if not goal.is_inconsistent_constraint(Constraint(action.slot,\
                            action.value)):
                            cleaned_stack.insert(0, action)
                    else:
                        cleaned_stack.insert(0, action)
        self.stack = cleaned_stack
Beispiel #4
0
    def respond(self):
        """Gets n actions from the agenda, where n is drawn depending on the agenda or a pdf."""
        # get some actions from the agenda

        assert len(
            self.agenda
        ) > 0, "Agenda is empty, this must not happen at this point!"

        if self.num_actions_next_turn > 0:
            # use and reset self.num_actions_next_turn if set
            num_actions = self.num_actions_next_turn
            self.num_actions_next_turn = -1
        elif self.agenda.stack[-1].type == UserActionType.Bye:
            # pop all actions from agenda since agenda can only contain thanks (optional) and
            # bye action
            num_actions = -1
        else:
            # draw amount of actions
            num_actions = min(len(self.agenda),
                              common.numpy.random.choice(
                                  [1, 2, 3], p=[.6, .3, .1]))  # hardcoded pdf

        # get actions from agenda
        user_actions = self.agenda.get_actions(num_actions)
        # copy needed for repeat action since they might be changed in other modules
        self.last_user_actions = copy.deepcopy(user_actions)

        for action in user_actions:
            if action.type == UserActionType.Inform:
                _constraint = Constraint(action.slot, action.value)
                # if _constraint in self.goal.constraints:
                if action in self.goal.missing_informs:
                    self.goal.missing_informs.remove(action)

        return user_actions
Beispiel #5
0
    def _receive_select(self, sys_act: SysAct):
        """Processes a select action from the system."""
        # handle as request
        value_in_goal = False
        for slot, values in sys_act.slot_values.items():
            for value in values:
                # do not consider 'dontcare' as any value
                if not self.goal.is_inconsistent_constraint_strict(
                        Constraint(slot, value)):
                    value_in_goal = True

        if value_in_goal:
            self._receive_request(sys_act)
        else:
            assert len(sys_act.slot_values.keys()) == 1,\
                "There shall be only one slot in a select action."
            slot = list(sys_act.slot_values.keys())[0]
            # inform about correct value with some probability
            if common.random.random(
            ) < self.parameters['usermodel']['InformOnSelect']:
                self.agenda.push(UserAct(act_type=UserActionType.Inform, slot=slot,\
                    value=self.goal.get_constraint(slot), score=1.0))

            for slot, values in sys_act.slot_values.items():
                for value in values:
                    self.agenda.push(UserAct(act_type=UserActionType.NegativeInform, slot=slot,\
                        value=value, score=1.0))
Beispiel #6
0
 def _receive_confirm(self, sys_act):
     """Processes a confirm action from the system."""
     for slot, _value in sys_act.slot_values.items():
         value = _value[0]  # there is always only one value
         if self.goal.is_inconsistent_constraint_strict(
                 Constraint(slot, value)):
             # inform about correct value with some probability, otherwise deny value
             if common.random.random(
             ) < self.parameters['usermodel']['InformOnConfirm']:
                 self.agenda.push(
                     UserAct(act_type=UserActionType.Inform, slot=slot,\
                     value=self.goal.get_constraint(slot), score=1.0))
             else:
                 self.agenda.push(UserAct(act_type=UserActionType.NegativeInform, slot=slot,\
                 value=value, score=1.0))
         else:
             # NOTE using inform currently since NLU currently does not support Affirm here and
             # NLU would tinker it into an Inform action anyway
             # self.agenda.push(
             #     UserAct(act_type=UserActionType.Affirm, score=1.0))
             self.agenda.push(
                 UserAct(act_type=UserActionType.Inform,
                         slot=slot,
                         value=value,
                         score=1.0))
Beispiel #7
0
    def _receive_informbyname(self, sys_act):
        """Processes an informbyname action from the system."""
        # check all system informs for offer
        inform_list = []
        offers = []
        for slot, value_list in sys_act.slot_values.items():
            for value in value_list:
                if slot == 'name':
                    offers.append(value)
                else:
                    inform_list.append(Constraint(slot, value))

        # check offer
        if offers:
            if self._check_offer(offers, inform_list):
                # valid offer
                for slot, value in inform_list:
                    self.goal.fulfill_request(slot, value)

        # needed to make sure that not informed constraints (which have been turned into requests)
        # will be asked first (before ending the dialog too early)
        req_actions_not_in_goal = []
        for action in self.agenda.get_actions_of_type(UserActionType.Request):
            if not action.slot in self.goal.requests:
                req_actions_not_in_goal.append(copy.deepcopy(action))

        # goal might be fulfilled now
        if (self.goal.is_fulfilled() and
                not self.agenda.contains_action_of_type(UserActionType.Inform)
                and not req_actions_not_in_goal):
            self._finish_dialog()
Beispiel #8
0
 def set_tools_constraints(self):
     for constr in self.rules["cell_constraints"]["tools_availability"]:
         if self.room == constr["room"]:
             c = Constraint("A", day=self.day,
                            time_slot=self.time_slot,
                            room=constr["room"], tools=constr["tools"], weight=1)
             self.C.append(c)
Beispiel #9
0
    def build_intrinsic_constraints(self):
        self.CI = []

        if self.type == "T":
            for constr in self.rules["ra_constraints"][self.type][str(
                    self.id)]["constraints"]:
                c = Constraint(self.type,
                               day=constr["day"],
                               time_slot=constr["time_slot"],
                               teacher=constr["teacher"])
                self.CI.append(c)
        else:
            for constr in self.rules["ra_constraints"][self.type][str(
                    self.id)]["constraints"]:
                c = Constraint(self.type,
                               tc_avail=constr["T_courses_availability"])
                self.CI.append(c)
Beispiel #10
0
 def set_unavailability_constraints(self):
     for constr in self.rules["cell_constraints"]["unavailability"]:
         if constr["day"] == self.day and \
                 constr["time_slot"] == self.time_slot and \
                 constr["room"] == self.room:
             c = Constraint("U", day=constr["day"],
                            time_slot=constr["time_slot"],
                            teacher=constr["room"], weight=1)
             self.C.append(c)
Beispiel #11
0
 def __init__(self, day, room, time_slot, rules):
     self.day = day
     self.room = room
     self.time_slot = time_slot
     self.rules = rules
     self.C = [Constraint("R", day=day, time_slot=time_slot, room=room)]
     self.bas = []
     self.reservation = None
     self.set_unavailability_constraints()
     self.set_tools_constraints()
     self.f = open("messages.txt", "a")
Beispiel #12
0
 def process_messages(self):
     while self.mq:
         message = self.mq.popleft()
         if message.type == 'partnership':
             if self.type == 'SG' and message.sender.type == 'SG':
                 sg = message.sender
                 t = message.partner_of_cell
                 print(
                     "[Message] RA {} received partnership message from sg {} about adding constraint from teacher {}"
                     .format(self.name, sg.name, t.ra_id))
                 self.add_induced_constraint(
                     Constraint("B",
                                teacher=t.ra_id,
                                owner_name=sg.name,
                                owner=sg))
         if message.type == 'partnership_cancelation':
             if self.type == 'SG' and message.sender.type == 'SG':
                 sg = message.sender
                 t = message.partner_of_cell
                 print(
                     "[Message] RA {} received partnership cancelation message from sg {} about removing constraint from teacher {}"
                     .format(self.name, sg.name, t.ra_id))
                 self.remove_induced_constraint("B", sg.name)
         if message.type == 'reservation':
             print(
                 "[Message] RA {} received reservation message from {} about reserving cell DAY {} TIME {} ROOM {}"
                 .format(self.name, message.sender.name,
                         message.partner_of_cell.day,
                         message.partner_of_cell.time_slot,
                         message.partner_of_cell.room))
             self.add_induced_constraint(
                 Constraint("I",
                            cell=message.partner_of_cell,
                            owner_name=message.sender.name,
                            owner=message.sender))
         if message.type == 'reservation_cancelation':
             print(
                 "[Message] RA {} received reservation cancelation message from {}"
                 .format(self.name, message.sender.name))
             self.remove_induced_constraint("I", message.sender.name)
Beispiel #13
0
 def set_reservation(self, cell):
     self.cancel_reservation()
     if cell.reservation is not None and cell.reservation != self.partnership:
         cell.reservation.cancel_reservation()
     self.reservation = True
     self.rCell = cell
     if cell.reservation is None:
         cell.reservation = self
     if cell.reservation == self:
         self.CR = [
             Constraint("R",
                        day=cell.day,
                        time_slot=cell.time_slot,
                        room=cell.room)
         ]
Beispiel #14
0
    def _check_offer(self, offers, informed_constraints_by_system):
        """ Checks for an offer and returns True if the offer is valid. """

        if not self._check_informs(informed_constraints_by_system):
            # reset offer in goal since inconsistencies have been detected and covered
            self.goal.requests[self.domain.get_primary_key()] = None
            return False

        # TODO maybe check for current offer first since alternative with name='none' by system
        # would trigger goal change -> what is the correct action in this case?
        if offers:
            if 'none' not in offers:
                # offer was given

                # convert informs of values != 'dontcare' to requests
                actions_to_convert = list(self.agenda.get_actions_of_type(UserActionType.Inform,\
                    consider_dontcare=False))
                if len(self.goal.constraints) > 1 and len(
                        actions_to_convert) == len(self.goal.constraints):
                    # penalise too early offers
                    self._repeat_last_actions()
                    self.num_actions_next_turn = len(self.last_user_actions)
                    return False

                # ask for values of remaining inform slots on agenda - this has two purposes:
                #   1. making sure that offer is consistent with goal
                #   2. making sure that inconsistent offers prolongate a dialog
                for action in actions_to_convert:
                    self.agenda.push(UserAct(act_type=UserActionType.Request, slot=action.slot,\
                        value=None, score=1.0))
                self.agenda.remove_actions_of_type(UserActionType.Inform)

                if self.goal.requests[
                        self.domain.get_primary_key()] is not None:
                    if self.goal.requests[
                            self.domain.get_primary_key()] in offers:
                        # offer is the same, don't change anything but treat offer as valid
                        return True
                    else:
                        # offer is not the same, but did not request a new one
                        # NOTE with current bst do not (negative) inform about the offer, because
                        # it will only set the proability to zero -> will not be excluded
                        # self.agenda.push(UserAct(act_type=UserActionType.NegativeInform,\
                        #     slot=self.domain.get_primary_key(), value=offers[0]))
                        return False
                else:
                    for _offer in offers:
                        if _offer not in self.excluded_venues:
                            # offer is not on the exclusion list (e.g. from reqalt action) and
                            # there is no current offer

                            # sometimes ask for alternative
                            if common.random.random(
                            ) < self.parameters['usermodel']['ReqAlt']:
                                self._request_alt(_offer)
                                return False
                            else:
                                self.goal.requests[
                                    self.domain.get_primary_key()] = _offer
                                for _action in self.goal.missing_informs:
                                    # informed constraints by system are definitely consistent with
                                    # goal at this point
                                    if Constraint(
                                            _action.slot, _action.value
                                    ) not in informed_constraints_by_system:
                                        self.agenda.push(
                                            UserAct(act_type=UserActionType.
                                                    Request,
                                                    slot=_action.slot,
                                                    value=None))
                                return True

                    # no valid offer was given
                    self._request_alt()
                    return False
            else:
                # no offer was given
                # TODO add probability to choose number of alternations
                altered_constraints = self._alter_constraints(
                    informed_constraints_by_system, 1)
                # reset goal push new actions on top of agenda
                self.goal.reset()
                self.goal.missing_informs = [
                    UserAct(act_type=UserActionType.Inform,
                            slot=_constraint.slot,
                            value=_constraint.value)
                    for _constraint in self.goal.constraints
                ]
                for _constraint in altered_constraints:
                    self.agenda.push(UserAct(act_type=UserActionType.Inform,\
                        slot=_constraint.slot, value=_constraint.value, score=1.0))
                self.agenda.clean(self.goal)
                return False
        return False
Beispiel #15
0
    def add_cell_unavailability_constraint(self, constr):
        c = Constraint("U", day=constr["day"],
                       time_slot=constr["time_slot"],
                       teacher=constr["room"], weight=1)

        self.C.append(c)