def discover_constraints(self) -> Set[AtomicConstraint]: """ Attempts to deduce new constraints from the current knowledge base. see :ref:`constraint-discovery` for more information. :return: A set containing all the newly discovered constraints. :rtype: Set[AtomicConstraint] """ new_constraints = set() for issue in self.neg_space.keys(): best_case = sum([ bc for i, bc in self.max_utility_by_issue.items() if i != issue ]) for value in self.neg_space[issue]: atom = atom_from_issue_value(issue, value) if atom in self.utilities.keys(): value_util = self.utilities[atom] else: value_util = 0 if best_case + value_util < self.acceptability_threshold: new_constraints.add(AtomicConstraint(issue, value)) return new_constraints
def get_problog_dists(self) -> str: """ Formats the offer as a distribution over the atomic issue value pairs. This is just so ProbLog agents can reason about them. >>> Offer(atomic).get_problog_dists() 0.0::First_A;1.0::First_B. 1.0::Second_C;0.0::Second_D. :return: A string expressing the offer in valid ProbLog as \ a distribution over the atomic issue value pairs. :rtype: str """ return_string = "" for issue in self.values_by_issue.keys(): atom_list: List[str] = [] for value in self.values_by_issue[issue].keys(): atom = atom_from_issue_value(issue, value) atom_list.append("{prob}::{atom}".format( prob=self.values_by_issue[issue][value], atom=atom)) return_string += ";".join(atom_list) + ".\n" return return_string
def calc_assignment_util(self, issue: str, value: str) -> float: """ calculates the utility gained from a perticular signed assignment asignements with unknown utilities are assigned 0 utility >>> print(self.utilities) {"boolean_False": -10, "boolean_True":10, "integer_3": 100} >>> self.calc_assignment_util("boolean","False") -10 >>> self.calc_assignment_util("unknown","unknown") 0 :param issue: the issue the assignment is associated with :type issue: str :param value: the value of the potential assignement :type value: str :return: the utility of the experiement :rtype: float """ chosen_atom = atom_from_issue_value(issue, value) if chosen_atom in self.utilities.keys(): return self.issue_weights[issue] * \ self.utilities[chosen_atom] return 0
def add_constraint(self, constraint: AtomicConstraint) -> bool: self.constraints.add(constraint) self.evaluator.add_constraint(constraint) self._add_utilities({ atom_from_issue_value(constraint.issue, constraint.value): self.constr_value }) self._index_max_utilities() self.init_generator() return self.constraints_satisfiable
def _compile_dtproblog_model(self): model_string = super()._compile_dtproblog_model() constr_string = "" for constr in self.constraints: constr_atom = atom_from_issue_value(constr.issue, constr.value) constr_string += "utility({},{}).\n".format(constr_atom, self.non_agreement_cost) return model_string + constr_string
def add_constraint(self, constraint: AtomicConstraint) -> bool: self.constraints.add(constraint) constraint_atom = atom_from_issue_value(constraint.issue, constraint.value) self._add_utilities({constraint_atom: self.constr_value}) for issue in self.neg_space.keys(): if not self.get_unconstrained_values_by_issue(issue): self.constraints_satisfiable = False return False return True
def add_constraints(self, new_constraints: Set[AtomicConstraint]) -> bool: self.constraints.update(new_constraints) self.evaluator.add_constraints(new_constraints) self._add_utilities({ atom_from_issue_value(constr.issue, constr.value): self.constr_value for constr in new_constraints }) self._index_max_utilities() self.init_generator() return self.constraints_satisfiable
def add_constraints(self, new_constraints: Iterable[AtomicConstraint]) -> bool: self.constraints.update(new_constraints) self._add_utilities({ atom_from_issue_value(constraint.issue, constraint.value): self.constr_value for constraint in self.constraints }) for issue in self.neg_space.keys(): if not self.get_unconstrained_values_by_issue(issue): self.constraints_satisfiable = False return False return True
def _compile_dtproblog_model(self) -> str: """ Takes the internal knowledge of the agent and complies it into a string representing a valid DTProbLog model that it can use to generate the next offer. :return: A string representing a valid DTProbLog model :rtype: str """ dtp_decision_vars = "" for issue in self.neg_space.keys(): atom_list = [] for value in self.neg_space[issue]: atom_list.append("?::{atom}".format( atom=atom_from_issue_value(issue, value))) dtp_decision_vars += ";".join(atom_list) + ".\n" utility_string = "" for utility, reward in self.utilities.items(): utility_string += "utility({},{}).\n".format(utility, reward) # we have to make sure we offset the utility of previously generated # offers so dtproblog won't generate them again offer_count = 0 for sparse_offer, util in self.generated_offers.items(): config_string = ",".join( list( map(lambda x: atom_from_issue_value(x[0], x[1]), sparse_offer))) + "." utility_string += "offer{} :- {}\n".format(offer_count, config_string) utility_string += "utility(offer{},{}).\n".format( offer_count, -util + self.non_agreement_cost) offer_count += 1 kb_string = "\n".join(self.knowledge_base) + "\n" return dtp_decision_vars + kb_string + utility_string
def calc_strat_utility(self, strat: Strategy) -> float: """ Calculate the expected utility of a strategy, meaning the expected utility of an offer that is sampled from this strattegy under the current knowledge base. :param strat: The strat to calculate the expected utility. :type strat: Strategy :return: The expected utility of the strategy under the current knowledge base \ and utilities. :rtype: float """ score = 0.0 for issue in strat.get_issues(): for value, prob in strat.get_value_dist(issue).items(): atom = atom_from_issue_value(issue, value) if atom in self.utilities.keys(): score += self.utilities[atom] * prob return score
def calc_strat_utility(self, strat: Strategy) -> float: """ Generalisation of an offer but works similarly. calculates the expceted utility under the strategy distribution. see :class:`Strategy` for more information. :param strat: the strategy to calculate the utility of :type strat: Strategy :return: expected utility under the given strategy with \ current knowledge base :rtype: float """ score = 0.0 for issue in strat.get_issues(): for value, prob in strat.get_value_dist(issue).items(): atom = atom_from_issue_value(issue, value) if atom in self.utilities.keys(): score += self.issue_weights[issue] * \ self.utilities[atom] * prob return score
def _index_max_utilities(self): """ Index the currently known utilities so we can use this as a heuristic during the constraint discovery process. See :ref:`constraint-discovery` for more information. """ self.max_utility_by_issue = { issue: 0 for issue in self.neg_space.keys() } for issue in self.neg_space.keys(): if self.sorted_utils[issue]: unconstrained_values = self.get_unconstrained_values_by_issue( issue) if not unconstrained_values: self.constraints_satisfiable = False return best_val = self.sorted_utils[issue][0] best_val_atom = atom_from_issue_value(issue, best_val) if best_val in unconstrained_values and best_val_atom in self.utilities.keys( ): self.max_utility_by_issue[issue] = self.utilities[ best_val_atom] self.max_util = sum(self.max_utility_by_issue.values())
def calc_assignment_util(self, issue: str, value: str) -> float: atom = atom_from_issue_value(issue, value) if atom in self.utilities.keys(): return self.utilities[atom] return 0.0