Example #1
0
    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
Example #2
0
    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
Example #3
0
    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
Example #4
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
Example #7
0
 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
Example #9
0
    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
Example #10
0
    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
Example #11
0
    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
Example #12
0
    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())
Example #13
0
    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