Skip to content

Commit

Permalink
fix conflicts
Browse files Browse the repository at this point in the history
  • Loading branch information
erelsgl committed Apr 14, 2024
1 parent b8bd146 commit c467615
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 9 deletions.
12 changes: 7 additions & 5 deletions fairpyx/algorithms/algorithm_examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
def algorithm1(alloc: AllocationBuilder):
"""
This dummy algorithm gives one item to the first agent, and all items to the second agent.
This algorithm ignores agent and item conflicts.
>>> divide(algorithm1, example_instance)
{'Alice': ['c1'], 'Bob': ['c1', 'c2', 'c3'], 'Chana': [], 'Dana': []}
"""
Expand All @@ -40,15 +41,16 @@ def algorithm1(alloc: AllocationBuilder):

def algorithm2(alloc: AllocationBuilder):
"""
This is a serial dictatorship algorithm: it lets each agent in turn pick all remaining items.
This is a serial dictatorship algorithm: it lets each agent in turn
pick all remaining items that have no agent or item conflicts.
>>> divide(algorithm2, example_instance)
{'Alice': ['c1', 'c2'], 'Bob': ['c1', 'c2', 'c3'], 'Chana': ['c2', 'c3'], 'Dana': ['c3']}
"""
logger.info("\nAlgorithm 2 starts. items %s , agents %s", alloc.remaining_item_capacities, alloc.remaining_agent_capacities)
picking_order = list(alloc.remaining_agents())
for agent in picking_order:
bundle = list(alloc.remaining_items())
bundle = list(alloc.remaining_items_for_agent(agent)) # remaining_items_for_agent returns all items that the agent can pick (i.e., all items with remaining capacities that the agent does not already have, and have no agent or item conflits).
agent_capacity = alloc.remaining_agent_capacities[agent]
if agent_capacity >= len(bundle):
alloc.give_bundle(agent, bundle)
Expand All @@ -64,13 +66,13 @@ def algorithm3(alloc: AllocationBuilder):
It first allocates one high-value item to each agent, and then run algorithm2.
>>> divide(algorithm3, example_instance)
{'Alice': ['c1', 'c3'], 'Bob': ['c1', 'c2'], 'Chana': ['c2', 'c3'], 'Dana': ['c3']}
{'Alice': ['c1', 'c3'], 'Bob': ['c1', 'c2', 'c3'], 'Chana': ['c2', 'c3'], 'Dana': ['c2', 'c3']}
"""
logger.info("\nAlgorithm 3 starts. items %s , agents %s", alloc.remaining_item_capacities, alloc.remaining_agent_capacities)

logger.info("\nFirst phase: allocate one high-value item per agent.")
for agent in list(alloc.remaining_agents()):
for item in list(alloc.remaining_items()):
for item in list(alloc.remaining_items_for_agent(agent)):
value = alloc.effective_value(agent, item) # `effective_value` returns the agent's item to the value if there is no conflict; otherwise, it returns minus infinity.
if value >= 10:
logger.info("%s gets %s, with value %s", agent, item, value)
Expand Down
5 changes: 3 additions & 2 deletions fairpyx/algorithms/iterated_maximum_matching.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ def _(code:str): return TEXTS[code][explanation_logger.language]
agents=alloc.remaining_agents(),
agent_capacity=lambda _:1,
agent_item_value=agent_item_value_with_bonus)

explanation_logger.debug("map_agent_to_bundle: %s", map_agent_to_bundle)

agents_with_empty_bundles = [agent for agent,bundle in map_agent_to_bundle.items() if len(bundle)==0]
Expand All @@ -126,7 +126,8 @@ def _(code:str): return TEXTS[code][explanation_logger.language]
for agent in map_agent_to_item.keys()
}
for agent,item in map_agent_to_item.items():
alloc.give(agent,item)
if (agent,item) not in alloc.remaining_conflicts:
alloc.give(agent,item)
if alloc.remaining_items():
for agent,item in map_agent_to_item.items():
explanation_logger.info(_("your_course_this_iteration"), map_agent_to_max_possible_value[agent], item, map_agent_to_value[agent], agents=agent)
Expand Down
12 changes: 11 additions & 1 deletion fairpyx/algorithms/picking_sequence.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def picking_sequence(alloc: AllocationBuilder, agent_order:list):
break
if not agent in alloc.remaining_agent_capacities:
continue
potential_items_for_agent = set(alloc.remaining_items()).difference(alloc.bundles[agent])
potential_items_for_agent = set(alloc.remaining_items_for_agent(agent))
if len(potential_items_for_agent)==0:
logger.info("Agent %s cannot pick any more items: remaining=%s, bundle=%s", agent, alloc.remaining_item_capacities, alloc.bundles[agent])
alloc.remove_agent_from_loop(agent)
Expand Down Expand Up @@ -81,6 +81,16 @@ def round_robin(alloc: AllocationBuilder, agent_order:list=None):
>>> instance = Instance(agent_capacities=agent_capacities, item_capacities=course_capacities, valuations=valuations)
>>> divide(round_robin, instance=instance)
{'Alice': ['c1', 'c2'], 'Bob': ['c1', 'c2', 'c3'], 'Chana': ['c2', 'c3'], 'Dana': ['c3']}
# Agent conflicts:
>>> instance = Instance(agent_capacities=agent_capacities, item_capacities=course_capacities, valuations=valuations, agent_conflicts={"Alice": ['c1', 'c2']})
>>> divide(round_robin, instance=instance)
{'Alice': ['c3'], 'Bob': ['c1', 'c2', 'c3'], 'Chana': ['c2', 'c3'], 'Dana': ['c1', 'c2', 'c3']}
# Item conflicts:
>>> instance = Instance(agent_capacities=agent_capacities, item_capacities=course_capacities, valuations=valuations, item_conflicts={"c1": ['c2'], "c2": ['c1']})
>>> divide(round_robin, instance=instance)
{'Alice': ['c1', 'c3'], 'Bob': ['c1', 'c3'], 'Chana': ['c2', 'c3'], 'Dana': ['c2', 'c3']}
"""
if agent_order is None: agent_order = list(alloc.remaining_agents())
picking_sequence(alloc, agent_order)
Expand Down
10 changes: 9 additions & 1 deletion fairpyx/allocations.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,12 +148,18 @@ def isdone(self)->bool:
"""
return len(self.remaining_item_capacities) == 0 or len(self.remaining_agent_capacities) == 0


def remaining_items(self)->list:
"""
Return the items with positive remaining capacity.
"""
return self.remaining_item_capacities.keys()

def remaining_items_for_agent(self, agent)->list:
"""
Return the items with positive remaining capacity, that are available for the agent
(== the agent does not already have them, and there are no item-conflicts or agent-conflicts)
"""
return [item for item in self.remaining_items() if (agent,item) not in self.remaining_conflicts]

def remaining_agents(self)->list:
"""
Expand Down Expand Up @@ -202,6 +208,8 @@ def give(self, agent:any, item:any, logger=None):
raise ValueError(f"Agent {agent} has no remaining capacity for item {item}")
if item not in self.remaining_item_capacities:
raise ValueError(f"Item {item} has no remaining capacity for agent {agent}")
if (agent,item) in self.remaining_conflicts:
raise ValueError(f"Agent {agent} is not allowed to take item {item} due to a conflict")
self.bundles[agent].add(item)
if logger is not None:
logger.info("Agent %s takes item %s with value %s", agent, item, self.instance.agent_item_value(agent, item))
Expand Down

0 comments on commit c467615

Please sign in to comment.