Skip to content

Commit

Permalink
Fix in cohesiveness
Browse files Browse the repository at this point in the history
  • Loading branch information
Simon-Rey committed Oct 24, 2023
1 parent 76916af commit eb98df4
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 21 deletions.
6 changes: 3 additions & 3 deletions pabutools/analysis/cohesiveness.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def is_cohesive_approval(
projects: Iterable[Project],
ballots: Iterable[AbstractApprovalBallot]
) -> bool:
if not is_large_enough(profile.num_ballots(), sum(profile.multiplicity(b) for b in ballots), total_cost(projects), instance.budget_limit):
if not is_large_enough(sum(profile.multiplicity(b) for b in ballots), profile.num_ballots(), total_cost(projects), instance.budget_limit):
return False
if len(ballots) == 0 or len(projects) == 0:
return False
Expand All @@ -34,13 +34,13 @@ def is_cohesive_cardinal(
ballots: Iterable[AbstractCardinalBallot],
alpha: dict[Project, Number]
) -> bool:
if not is_large_enough(profile.num_ballots(), sum(profile.multiplicity(b) for b in ballots), total_cost(projects), instance.budget_limit):
if not is_large_enough(sum(profile.multiplicity(b) for b in ballots), profile.num_ballots(), total_cost(projects), instance.budget_limit):
return False
if len(ballots) == 0 or len(projects) == 0:
return False
for ballot in ballots:
for p in projects:
if ballot[p] > alpha[p]:
if ballot[p] < alpha[p]:
return False
return True

Expand Down
150 changes: 132 additions & 18 deletions pabutools/analysis/justifiedrepresentation.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,31 @@
from collections.abc import Iterable
from collections.abc import Iterable, Callable
from numbers import Number

from pabutools.analysis.cohesiveness import cohesive_groups
from pabutools.analysis.cohesiveness import cohesive_groups, is_large_enough
from pabutools.election import Instance, AbstractApprovalProfile, Project, SatisfactionMeasure, Additive_Cardinal_Sat, \
AbstractCardinalProfile
AbstractCardinalProfile, ApprovalBallot, total_cost
from pabutools.utils import powerset


def is_in_core_approval(
instance: Instance,
profile: AbstractApprovalProfile,
sat_class: type[SatisfactionMeasure],
budget_allocation: Iterable[Project]
) -> bool:
for group in powerset(profile):
if len(group) > 0:
for proj_set in powerset(instance):
if is_large_enough(len(group), profile.num_ballots(), total_cost(proj_set), instance.budget_limit):
all_better_alone = True
for ballot in group:
sat = sat_class(instance, profile, ballot)
if sat.sat(budget_allocation) >= sat.sat(proj_set):
all_better_alone = False
break
if all_better_alone:
return False
return True


def is_strong_EJR_approval(
Expand All @@ -13,7 +36,8 @@ def is_strong_EJR_approval(
) -> bool:
for group, project_set in cohesive_groups(instance, profile):
all_agents_sat = True
for sat in profile.as_sat_profile(sat_class):
for ballot in group:
sat = sat_class(instance, profile, ballot)
if sat.sat(budget_allocation) < sat.sat(project_set):
all_agents_sat = False
break
Expand All @@ -26,43 +50,91 @@ def is_EJR_approval(
instance: Instance,
profile: AbstractApprovalProfile,
sat_class: type[SatisfactionMeasure],
budget_allocation: Iterable[Project]
budget_allocation: Iterable[Project],
up_to_func: Callable[[Iterable[Number]], Number] = None
) -> bool:
for group, project_set in cohesive_groups(instance, profile):
one_agent_sat = False
for sat in profile.as_sat_profile(sat_class):
if sat.sat(budget_allocation) >= sat.sat(project_set):
for ballot in group:
sat = sat_class(instance, profile, ballot)
surplus = 0
if up_to_func is not None:
surplus = up_to_func(sat.sat_project(p) for p in project_set if p not in budget_allocation)
if sat.sat(budget_allocation) + surplus >= sat.sat(project_set):
one_agent_sat = True
break
if not one_agent_sat:
return False
return True


def is_PJR_approval(
def is_EJR_any_approval(
instance: Instance,
profile: AbstractApprovalProfile,
sat_class: type[SatisfactionMeasure],
budget_allocation: Iterable[Project]
) -> bool:
return is_EJR_approval(instance, profile, sat_class, budget_allocation, up_to_func=lambda x: min(x, default=0))


def is_EJR_one_approval(
instance: Instance,
profile: AbstractApprovalProfile,
sat_class: type[SatisfactionMeasure],
budget_allocation: Iterable[Project]
) -> bool:
return is_EJR_approval(instance, profile, sat_class, budget_allocation, up_to_func=lambda x: max(x, default=0))


def is_PJR_approval(
instance: Instance,
profile: AbstractApprovalProfile,
sat_class: type[SatisfactionMeasure],
budget_allocation: Iterable[Project],
up_to_func: Callable[[Iterable[Number]], Number] = None
) -> bool:
for group, project_set in cohesive_groups(instance, profile):
threshold = sat_class(instance, profile, project_set).sat(project_set)
sat = sat_class(instance, profile, ApprovalBallot(instance))
threshold = sat.sat(project_set)
group_approved = {p for p in budget_allocation if any(p in b for b in profile)}
group_sat = sat_class(instance, profile, group_approved).sat(group_approved)
if not group_sat < threshold:
surplus = 0
if up_to_func is not None:
surplus = up_to_func(sat.sat_project(p) for p in project_set if p not in budget_allocation)
group_sat = sat.sat(group_approved) + surplus
if group_sat < threshold:
return False
return True


def is_PJR_any_approval(
instance: Instance,
profile: AbstractApprovalProfile,
sat_class: type[SatisfactionMeasure],
budget_allocation: Iterable[Project]
) -> bool:
return is_PJR_approval(instance, profile, sat_class, budget_allocation, up_to_func=lambda x: min(x, default=0))


def is_PJR_one_approval(
instance: Instance,
profile: AbstractApprovalProfile,
sat_class: type[SatisfactionMeasure],
budget_allocation: Iterable[Project]
) -> bool:
return is_PJR_approval(instance, profile, sat_class, budget_allocation, up_to_func=lambda x: max(x, default=0))


def is_strong_EJR_cardinal(
instance: Instance,
profile: AbstractCardinalProfile,
budget_allocation: Iterable[Project]
budget_allocation: Iterable[Project],
sat_class: type[SatisfactionMeasure] = Additive_Cardinal_Sat,
) -> bool:
for group, project_set in cohesive_groups(instance, profile):
all_agents_sat = True
threshold = sum(min(b[p] for b in profile) for p in project_set)
for sat in profile.as_sat_profile(Additive_Cardinal_Sat):
for ballot in group:
sat = sat_class(instance, profile, ballot)
if sat.sat(budget_allocation) < threshold:
all_agents_sat = False
break
Expand All @@ -74,28 +146,70 @@ def is_strong_EJR_cardinal(
def is_EJR_cardinal(
instance: Instance,
profile: AbstractCardinalProfile,
budget_allocation: Iterable[Project]
budget_allocation: Iterable[Project],
sat_class: type[SatisfactionMeasure] = Additive_Cardinal_Sat,
up_to_func: Callable[[Iterable[Number]], Number] = None
) -> bool:
for group, project_set in cohesive_groups(instance, profile):
one_agent_sat = False
threshold = sum(min(b[p] for b in profile) for p in project_set)
for sat in profile.as_sat_profile(Additive_Cardinal_Sat):
if sat.sat(budget_allocation) >= threshold:
for ballot in group:
sat = sat_class(instance, profile, ballot)
surplus = 0
if up_to_func is not None:
surplus = up_to_func(sat.sat_project(p) for p in project_set if p not in budget_allocation)
if sat.sat(budget_allocation) + surplus >= threshold:
one_agent_sat = True
break
if not one_agent_sat:
return False
return True


def is_EJR_one_cardinal(
instance: Instance,
profile: AbstractCardinalProfile,
budget_allocation: Iterable[Project],
) -> bool:
return is_EJR_cardinal(instance, profile, budget_allocation, up_to_func=lambda x: min(x, default=0))


def is_EJR_any_cardinal(
instance: Instance,
profile: AbstractCardinalProfile,
budget_allocation: Iterable[Project],
) -> bool:
return is_EJR_cardinal(instance, profile, budget_allocation, up_to_func=lambda x: max(x, default=0))


def is_PJR_cardinal(
instance: Instance,
profile: AbstractCardinalProfile,
budget_allocation: Iterable[Project]
budget_allocation: Iterable[Project],
up_to_func: Callable[[Iterable[Number]], Number] = None
) -> bool:
for group, project_set in cohesive_groups(instance, profile):
threshold = sum(min(b[p] for b in profile) for p in project_set)
group_sat = sum(max(b[p] for b in profile) for p in budget_allocation)
if group_sat < threshold:
surplus = 0
if up_to_func is not None:
surplus = up_to_func(max(b[p] for b in profile) for p in project_set if p not in budget_allocation)
if group_sat + surplus < threshold:
return False
return True


def is_PJR_any_cardinal(
instance: Instance,
profile: AbstractCardinalProfile,
budget_allocation: Iterable[Project],
) -> bool:
return is_PJR_cardinal(instance, profile, budget_allocation, up_to_func=lambda x: min(x, default=0))


def is_PJR_one_cardinal(
instance: Instance,
profile: AbstractCardinalProfile,
budget_allocation: Iterable[Project],
) -> bool:
return is_PJR_cardinal(instance, profile, budget_allocation, up_to_func=lambda x: max(x, default=0))

0 comments on commit eb98df4

Please sign in to comment.