def work_entity_retrieval(self, belief, global_summary): ''' ''' array_slot_summary = SummaryUtils.arraySlotSummary(belief, self.domainString) logger.debug(str(global_summary)) logger.debug('HDC policy: getGlobal') done, output = self._getGlobal(belief, global_summary) if not done: logger.debug('HDC policy: getConfirmSelect') done, output = self._getConfirmSelect(belief, array_slot_summary) if not done: logger.debug('HDC policy: getInform') inform_summary = [] for num_accepted in range(1, MAX_NUM_ACCEPTED+1): temp = SummaryUtils.actionSpecificInformSummary(belief, num_accepted, self.domainString) inform_summary.append(temp) done, output = self._getInform(belief, global_summary, inform_summary) if not done: logger.debug('HDC policy: getRequest') done, output = self._getRequest(belief, array_slot_summary) if not done: logger.warning("HDCPolicy couldn't find action: execute reqmore().") output = 'reqmore()' if output == 'badact()' or output == 'null()': logger.warning('HDCPolicy chose bad or null action') output = 'null()' if self.use_confreq: #TODO - known problem here if use_confreq is True (ie being used) FIXME output = PolicyUtils.add_venue_count(output, belief) return output
def _getInformAlternativeEntities(acceptanceList, acceptanceList80, prohibitedList, domainString): ''' Returns the dialogue act representing either 1) there is not matching venue: inform(name=none, slot=value, ...) 2) it offers a venue which is not on the prohibited list 3) if all matching venues are on the prohibited list then it says there is no venue except x,y,z,... with such features: inform(name=none, name!=x, name!=y, name!=z, ..., slot=value, ...) ''' acceptedValues = {} numFeats = len(acceptanceList80) for slot in acceptanceList80: (topvalue, topbelief) = acceptanceList80[slot] if topvalue != 'dontcare': acceptedValues[slot] = topvalue if len(acceptedValues) == 0: logger.warning("User didn't specify any constraints or all are dontcare") #return 'null()' result = Ontology.global_ontology.entity_by_features(domainString, acceptedValues) if len(result) == 0: return SummaryUtils.getInformNoneVenue(acceptedValues) else: for ent in result: name = ent['name'] if name not in prohibitedList: return getInformAcceptedSlotsAboutEntity(acceptanceList80, ent, numFeats) return SummaryUtils.getInformNoMoreVenues(acceptanceList, result) return 'null()'
def _getInformRequestedSlots(acceptanceList80, requestedSlots, name, domainString, bookinginfo=None): result = Ontology.global_ontology.entity_by_features(domainString, {'name':name}) print ("aaa", name) print ("bbb", domainString) acceptedValues = {} for slot in acceptanceList80: (topvalue, topbelief) = acceptanceList80[slot] if topvalue != 'dontcare': acceptedValues[slot] = topvalue # add bookinginfo info to acceptedValues if bookinginfo is not None: for slot,constraint in bookinginfo.iteritems(): if constraint is not None: acceptedValues[slot] = constraint if len(result) > 0 and name != 'none': # We found exactly one or more matching entities. Use the first one ent = result[0] # return SummaryUtils._getInformRequestedSlotsForEntity(acceptedValues, requestedSlots, ent) CHECK return SummaryUtils._getInformRequestedSlotsForEntity(requestedSlots, ent, domainString) else: logger.debug('Couldn\'t find the provided name ' + name) # We have not informed about an entity yet, or there are too many entities. return 'null()'
def arraySlotSummaryRel(belief, domainString): ''' Gets the summary vector for goal slots, including the top probabilities, entropy, etc. :param belief: dict representing the full belief state :param domainString: string representing the domain :return: (dict) of slot goal summaries ''' summary = {} slots = Ontology.global_ontology.get_common_slots_for_all_types( domainString) for domain in slots: for s in slots[domain]: slot = domain + '_' + s summary[slot] = {} slot_belief = belief['beliefs'][slot] summary[slot]['TOPHYPS'], summary[slot][ 'ISTOPNONE'] = SummaryUtils.getTopBeliefsExcludingNone( belief['beliefs'][slot]) belief_dist = slot_belief.values() summary[slot]['ENTROPY'] = entropy(belief_dist) summary[slot][ 'ISREQUESTTOP'] = belief['beliefs']['requested'][slot] > 0.5 return summary
def getInformExactEntity(acceptanceList, numAccepted, domainString): '''**Method for global inform action:** creates inform act with none or an entity :param acceptanceList: of slots with value:prob mass pairs :type acceptanceList: dict :param numAccepted: number of *accepted slots* (>80 prob mass) :type numAccepted: int :param domainString: domain tag :type domainString: str :returns: getInformNoneVenue() or getInformAcceptedSlotsAboutEntity() as appropriate ''' acceptedValues = {} for i, slot in enumerate(acceptanceList): if i >= numAccepted: break #(topvalue, topbelief) = acceptanceList[slot] (topvalue, _) = acceptanceList[slot] if topvalue != 'dontcare': acceptedValues[slot] = topvalue result = Ontology.global_ontology.entity_by_features(domainString, acceptedValues) if len(result) == 0: return SummaryUtils.getInformNoneVenue(acceptedValues) else: ent = result[0] return getInformAcceptedSlotsAboutEntity(acceptanceList, ent, numAccepted)
def _updateDBfeatures(self, belief, eType): features = [] for numAccepted in range(1, 6): temp = SummaryUtils.actionSpecificInformSummary( belief, numAccepted, eType) features += temp return features
def _getRequest(self, belief, array_slot_summary): ''' ''' # This is added for confreq. need_grounding = SummaryUtils.getTopBeliefs( belief, 0.8, domainString=self.domainString) for slot in Ontology.global_ontology.get_sorted_system_requestable_slots( self.domainString): summary = array_slot_summary[slot] (_, topprob) = summary['TOPHYPS'][0] #(_, secprob) = summary['TOPHYPS'][1] if topprob < 0.8: # Add implicit confirmation (for confreq.) grounding_slots = copy.deepcopy(need_grounding) if slot in grounding_slots: del grounding_slots[slot] grounding_result = [] for grounding_slot in grounding_slots: if len(grounding_result) < 3: (value, _) = grounding_slots[grounding_slot] #(value, prob) = grounding_slots[grounding_slot] grounding_result.append('%s="%s"' % (grounding_slot, value)) if not grounding_result or not self.use_confreq: return True, 'request(%s)' % slot else: return True, 'confreq(' + ','.join( grounding_result) + ',%s)' % slot return False, 'null()'
def getInformRequestedSlots(requested_slots, name, domainString): ''' Informs about the requested slots from the last informed venue of form the venue informed by name :param requested_slots: list of requested slots :param name: name of the last informed venue :param domainString: string representing the domain :return: string representing the inform dialogue act ''' result = Ontology.global_ontology.entity_by_features( domainString, {'name': name}) if len(result) > 0: ent = result[0] return _getInformRequestedSlotsForEntity(requested_slots, ent, domainString) else: if not name: # Return a random venue result = [] while len(result) == 0: rand_name = Ontology.global_ontology.getRandomValueForSlot( domainString, 'name', nodontcare=True) result = Ontology.global_ontology.entity_by_features( domainString, {'name': rand_name}) ent = result[0] return _getInformRequestedSlotsForEntity(requested_slots, ent, domainString) else: logger.warning('Couldn\'t find the provided name: ' + name) return SummaryUtils.getInformNoneVenue({'name': name})
def _getInform(self, belief, global_summary, inform_summary): act = 'null()' count80 = global_summary['GLOBAL_COUNTACCEPTED'] offer_happened = global_summary['GLOBAL_OFFERHAPPENED'] if count80 >= MAX_NUM_ACCEPTED: count80 = MAX_NUM_ACCEPTED - 1 arr = inform_summary[count80] first = arr[0] # True if there is no matching entities second = arr[1] # True if there is one matching entities #third = arr[2] # True if there is two~four matching entities discr = arr[4] # True if we can discriminate more logger.debug( '%d among %d slots are accepted (>=0.8 belief).' % (count80, Ontology.global_ontology.get_length_system_requestable_slots( self.domainString))) count80_logic = count80 >= Ontology.global_ontology.get_length_system_requestable_slots( self.domainString) if first or second or not discr or count80_logic: # If this inform gives either 0 or 1 or we've found everything we can ask about logger.debug( 'Trying to get inform action, have enough accepted slots.') logger.debug('Is there no matching entity? %s.' % str(first)) logger.debug('Is there only one matching entity? %s.' % str(second)) logger.debug('Can we discriminate more? %s.' % str(discr)) requested_slots = SummaryUtils.getRequestedSlots(belief) if len(requested_slots) > 0 and offer_happened: logger.debug('Getting inform requested action.') act = PolicyUtils.getGlobalAction( belief, 'INFORM_REQUESTED', domainString=self.domainString) else: logger.debug( 'Getting inform exact action with %d accepted slots.' % count80) act = PolicyUtils.getInformAction( count80, belief, domainString=self.domainString) if act != 'null()': return True, act return False, act
def getInformAction(numAccepted, belief, domainString): '''**Method for global inform action:** returns inform act via getInformExactEntity() method \ or null() if not enough accepted :param belief: full belief state :type belief: dict :param numAccepted: number of slots with prob. mass > 80 :type numAccepted: int :param domainString: domain tag :type domainString: str :returns: getInformExactEntity(acceptanceList,numAccepted) ''' acceptanceList = SummaryUtils.getTopBeliefs(belief, domainString=domainString) # dict containing the slots with top values diferent to **NONE** and their top values if numAccepted > len(acceptanceList): # ic340: When would this happen? return 'null()' return getInformExactEntity(acceptanceList, numAccepted, domainString)
def getInformAcceptedSlotsAboutEntity(acceptanceList, ent, numFeats): '''**Method for global inform action:** returns filled out inform() string need to be cleaned (Dongho) :param acceptanceList: of slots with value:prob mass pairs :type acceptanceList: dict :param ent: slot:value properties for this entity :type ent: dict :param numFeats: result of globalOntology.entity_by_features(acceptedValues) :type numFeats: int :returns: (str) filled out inform() act ''' ans = 'inform(' feats = {'name': ent['name']} acceptanceKeys = acceptanceList.keys() maxNumFeats = 5 if Settings.config.has_option("policy", "maxinformslots"): maxNumFeats = int(Settings.config.get('policy', 'maxinformslots')) if numFeats > maxNumFeats: Settings.random.shuffle(acceptanceKeys) acceptanceKeys = acceptanceKeys[:maxNumFeats] for i, slot in enumerate(acceptanceKeys): if i >= numFeats: break if slot == 'name': continue (value, belief) = acceptanceList[slot] if value == 'dontcare' and slot in ent and ent[slot] != "not available": feats[slot] = ent[slot] else: if slot in ent: feats[slot] = ent[slot]#value else: logger.debug('Slot %s is not found in data for entity %s' % (slot, ent['name'])) ans += SummaryUtils.convertFeatsToStr(feats) + ')' return ans
def add_venue_count(input, belief, domainString): '''Add venue count. :param input: String input act. :param belief: Belief state :param domainString: domain tag like 'SFHotels' :type domainString: str :returns: act with venue count. ''' acceptanceList = SummaryUtils.getTopBeliefs(belief, domainString) accepted_slots = {} for i, slot in enumerate(acceptanceList): (topvalue, topbelief) = acceptanceList[slot] if topvalue != 'dontcare': accepted_slots[slot] = topvalue count = Ontology.global_ontology.get_length_entity_by_features(domainString, accepted_slots) input_act = DiaAct.DiaAct(input) if input_act.act == 'confreq': if count > 1: output = copy.deepcopy(input_act) for slot in accepted_slots: val = accepted_slots[slot] if not input_act.contains(slot, val): output.append_front(slot, val) output.append_front('count', str(count)) return str(output) # else: # logger.warning('accepted slots: ' + str(accepted_slots)) # logger.error('badact in add_venue_count: input=%s, count=%d' % (input, count)) # return 'badact()' # elif count <=1 and len(accepted_slots) > 0 and input_act.act in ['confirm', 'request', 'select']: # logger.warning('accepted slots: ' + str(accepted_slots)) # logger.error('badact in add_venue_count: input=%s, count=%d' % (input, count)) # return 'badact()' return input
def _updateMactFeat(self, last_feature, lastact): ''' Add features into self.prevstate - recording actions taken by machine :param last_feature: last system state features :type last_feature: dict :param lastact: last system dialgoue act :type lastact: string :return: None ''' features = {} if self.turn == 0: features['lastInformedVenue'] = '' features['informedVenueSinceNone'] = [] features['lastActionInformNone'] = False features['offerHappened'] = False else: last_system_act = dact.ParseAct(lastact, user=False) # lastInformedVenue current_informed_venue = BeliefTrackingUtils._getCurrentInformedVenue(last_system_act) current_informed_venue = self._list2str_bugfix(current_informed_venue) if current_informed_venue != '': features['lastInformedVenue'] = current_informed_venue elif last_feature != None and last_feature['lastInformedVenue'] != None: features['lastInformedVenue'] = last_feature['lastInformedVenue'] # informedVenueSinceNone if last_feature['informedVenueSinceNone'] == None or \ last_system_act == None or BeliefTrackingUtils._hasType(last_system_act, 'canthelp'): informedVenueSinceNone = [] else: informedVenueSinceNone = last_feature['informedVenueSinceNone'] if BeliefTrackingUtils._hasTypeSlot(last_system_act, 'offer', 'name'): venue = BeliefTrackingUtils._getTypeSlot(last_system_act, 'offer', 'name') venue = self._list2str_bugfix(venue) informedVenueSinceNone.append(venue) features['informedVenueSinceNone'] = informedVenueSinceNone # lastActionInformNone if BeliefTrackingUtils._hasType(last_system_act, 'canthelp'): features['lastActionInformNone'] = True else: features['lastActionInformNone'] = False # offerhappened if BeliefTrackingUtils._hasTypeSlot(last_system_act, 'offer', 'name'): features['offerHappened'] = True else: features['offerHappened'] = False # inform_info features['inform_info'] = [] for numAccepted in range(1,6): temp = SummaryUtils.actionSpecificInformSummary(self.prevbelief, numAccepted, self.domainString) features['inform_info'] += temp self.prevbelief['features'] = features
def nextAction(self, belief): """Primary response function of HDC policy - hands off control to entity-retrieval policy. """ global_summary = SummaryUtils.globalSummary( belief, domainString=self.domainString) return self.work_entity_retrieval(belief, global_summary)
def getInformByName(self, belief): requested_slots = SummaryUtilsRel.getRequestedSlots(belief) name = SummaryUtils.getTopBelief(belief['beliefs']['name'])[0] if name == '**NONE**': name = belief['features']['lastInformedVenue'] return SummaryUtilsRel.getInformRequestedSlots(requested_slots, name, self.domainString)
def getNonExecutable(self, belief, lastSystemAction): ''' Set of rules defining the mask over the action set, given the current belief state :param belief: the current master belief :type belief: dict :param lastSystemAction: the system action of the previous turn :type lastSystemAction: string :return: list of non-executable (masked) actions ''' array_slot_summary = SummaryUtils.arraySlotSummary(belief, self.domainString) array_slot_summary_rel = SummaryUtilsRel.arraySlotSummaryRel(belief, self.domainString) global_summary = SummaryUtils.globalSummary(belief, self.domainString) if global_summary['GLOBAL_BYALTERNATIVES'] and not global_summary['GLOBAL_THANKYOU'] and not global_summary['GLOBAL_ACK']: self.alternatives_requested = True nonexec = ['pass'] for action in self.action_names: mask_action = False if 'rel' in action: if "request_" in action: nonexec.append(action) # pass # if mask_action and self.request_mask: # nonexec.append(action) elif "select_" in action: slot_summary = array_slot_summary_rel['_'.join(action.split('_')[2:])] top_prob = slot_summary['TOPHYPS'][0][1] sec_prob = slot_summary['TOPHYPS'][1][1] if top_prob == 0 or sec_prob == 0: mask_action = True if mask_action and self.request_mask: nonexec.append(action) elif "confirm_" in action: slot_summary = array_slot_summary_rel['_'.join(action.split('_')[2:])] top_prob = slot_summary['TOPHYPS'][0][1] if top_prob == 0: mask_action = True if mask_action and self.request_mask: nonexec.append(action) else: if action == "inform": acceptance_list = SummaryUtils.getTopBeliefs(belief, domainString=self.domainString) discriminable = SummaryUtils.acceptanceListCanBeDiscriminated(acceptance_list, self.domainString) if not global_summary['GLOBAL_BYCONSTRAINTS']: mask_action = True if global_summary['GLOBAL_COUNTACCEPTED'] < self.inform_count_accepted and discriminable: mask_action = True if mask_action and self.inform_mask: nonexec.append(action) elif action == "inform_byname": if not global_summary['GLOBAL_BYNAME']: mask_action = True if belief['features']['lastInformedVenue'] == '' \ and SummaryUtils.getTopBelief(belief['beliefs']['name'])[0] == '**NONE**' : mask_action = True if mask_action and self.inform_mask: nonexec.append(action) elif action == "inform_alternatives": if not self.alternatives_requested: mask_action = True if belief['features']['lastInformedVenue'] == '': mask_action = True if mask_action and self.inform_mask: nonexec.append(action) elif action == "bye": if not global_summary['GLOBAL_FINISHED']: mask_action = True if mask_action and self.bye_mask: nonexec.append(action) elif action == "repeat": if not global_summary['GLOBAL_REPEAT'] or lastSystemAction is None: mask_action = True mask_action = True # ic340: this action is "deactivated" because simuser doesnt know how to react to it if mask_action: nonexec.append(action) elif action == "reqmore": if belief['features']['lastInformedVenue'] == '': mask_action = True if mask_action and self.request_mask: nonexec.append(action) elif action == "restart": if not global_summary['GLOBAL_RESTART']: mask_action = True mask_action = True # ic340: this action is "deactivated" because simuser doesnt know how to react to it if mask_action: nonexec.append(action) elif "request_" in action: pass if mask_action and self.request_mask: nonexec.append(action) elif "select_" in action: slot_summary = array_slot_summary[action.split("_")[1]] top_prob = slot_summary['TOPHYPS'][0][1] sec_prob = slot_summary['TOPHYPS'][1][1] if top_prob == 0 or sec_prob == 0: mask_action = True if mask_action and self.request_mask: nonexec.append(action) elif "confirm_" in action: slot_summary = array_slot_summary[action.split("_")[1]] top_prob = slot_summary['TOPHYPS'][0][1] if top_prob == 0: mask_action = True if mask_action and self.request_mask: nonexec.append(action) elif "confreq_" in action: slot_summary = array_slot_summary[action.split("_")[1]] top_prob = slot_summary['TOPHYPS'][0][1] if top_prob == 0: mask_action = True if mask_action and self.request_mask: nonexec.append(action) logger.dial('masked inform actions:' + str([act for act in nonexec if 'inform' in act])) return nonexec
def getGlobalAction(belief, globalact, domainString): '''**Method for global action:** returns action :param belief: full belief state :type belief: dict :param globalact: - str of globalActionName, e.g. 'INFORM_REQUESTED' :type globalact: int :param domainString: domain tag :type domainString: str :returns: (str) action ''' # First get the name for the name goal. topvalue, topbelief = SummaryUtils.getTopBelief(belief['beliefs']['name']) toptwo, _ = SummaryUtils.getTopBeliefsExcludingNone(belief['beliefs']['name']) if topvalue == '**NONE**' or topvalue == 'dontcare' or topbelief < 0.8: topnamevalue = '' else: topnamevalue = toptwo[0][0] lastInformedVenue = belief['features']['lastInformedVenue'] informedVenueSinceNone = belief['features']['informedVenueSinceNone'] acceptanceList = SummaryUtils.getTopBeliefs(belief, domainString=domainString) inform_threshold = 0 if Settings.config.has_option("policy", "informthreshold"): inform_threshold = float(Settings.config.get('policy','informthreshold')) if inform_threshold > 0: acceptanceList80 = SummaryUtils.getTopBeliefs(belief, inform_threshold, domainString=domainString) else: acceptanceList80 = acceptanceList requestedSlots = SummaryUtils.getRequestedSlots(belief) # logger.debug('topnamevalue = %s, lastInformedVenue = %s' % (topnamevalue, lastInformedVenue)) if topnamevalue == '' and lastInformedVenue != '': # logger.debug('topnamevalue is None, but copy lastInformedVenue') topnamevalue = lastInformedVenue if globalact == 'INFORM_REQUESTED': if topnamevalue != '': return _getInformRequestedSlots(acceptanceList80, requestedSlots, topnamevalue, domainString) else: return _getInformRequestedSlots(acceptanceList80, requestedSlots, 'none', domainString) elif globalact == 'INFORM_ALTERNATIVES': #if lastInformedVenue == '': # print 'Cant inform alternatives no last informed venue' # return 'null()' #else: return _getInformAlternativeEntities(acceptanceList, acceptanceList80, informedVenueSinceNone, domainString) elif globalact == 'INFORM_MORE': #ic340: is this ever used? if len(informedVenueSinceNone) > 0 and topnamevalue != '': return _getInformMoreEntity(topnamevalue, domainString) else: return _getInformMoreEntity('none', domainString) elif globalact == 'INFORM_BYNAME': return _getInformAlternativeEntities(acceptanceList, acceptanceList80, [], domainString) elif globalact == 'INFORM_REPEAT': return 'null()' elif globalact == 'REQMORE': if lastInformedVenue != '': return 'reqmore()' else: return 'null()' elif globalact == 'BYE': return 'bye()' elif globalact == 'RESTART': return 'null()' else: logger.warning('Invalid global summary action name: ' + globalact) return 'null()'