From 7f62d8d062373416580c32045f403bab41cc8483 Mon Sep 17 00:00:00 2001 From: Abodi Massarwa Date: Wed, 31 Jul 2024 16:46:22 +0300 Subject: [PATCH 01/36] modified helper_validate_item_categories in heterogeneous_matroid_constraints_algorithms.py --- .../algorithms/heterogeneous_matroid_constraints_algorithms.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py index 4232d0b..5f16fd8 100644 --- a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py +++ b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py @@ -1187,6 +1187,8 @@ def helper_validate_item_categories(item_categories:dict[str, list]): for category, items in item_categories.items(): if not isinstance(category, str) or not isinstance(items, list): raise ValueError(f"item categories not structured properly!!!") + #else it's not of type dict ! error + raise ValueError(f"item categories is supposed to be dict[str,list] but u entered {type(item_categories)}") if __name__ == "__main__": import doctest, sys From be90398e501acc4f893d6080598e906c560bdafc Mon Sep 17 00:00:00 2001 From: Abodi Massarwa Date: Wed, 31 Jul 2024 16:54:10 +0300 Subject: [PATCH 02/36] modified helper_validate_item_categories in heterogeneous_matroid_constraints_algorithms.py --- .../heterogeneous_matroid_constraints_algorithms.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py index 5f16fd8..52bf9fb 100644 --- a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py +++ b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py @@ -1187,8 +1187,8 @@ def helper_validate_item_categories(item_categories:dict[str, list]): for category, items in item_categories.items(): if not isinstance(category, str) or not isinstance(items, list): raise ValueError(f"item categories not structured properly!!!") - #else it's not of type dict ! error - raise ValueError(f"item categories is supposed to be dict[str,list] but u entered {type(item_categories)}") + else: + raise ValueError(f"item categories is supposed to be dict[str,list] but u entered {type(item_categories)}") if __name__ == "__main__": import doctest, sys From d20006bff9acc631418935fc34d007925d19a4cb Mon Sep 17 00:00:00 2001 From: Abodi Massarwa Date: Sat, 3 Aug 2024 13:50:05 +0300 Subject: [PATCH 03/36] minor change in validate_capacities --- .../algorithms/heterogeneous_matroid_constraints_algorithms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py index 52bf9fb..7638d04 100644 --- a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py +++ b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py @@ -1166,7 +1166,7 @@ def helper_validate_capacities(agent_category_capacities: dict[str, dict[str, in first_agent_capacities = next(iter(agent_category_capacities.values())) for agent, capacities in agent_category_capacities.items(): if capacities != first_agent_capacities: - raise ValueError(f"Capacities for agent {agent} are not identical.") + raise ValueError(f"Capacities for {agent}={capacities} are not identical with {list(agent_category_capacities.keys())[0]}={first_agent_capacities}.") # Check if there are negative capacities negative_capacities = [value for agent in agent_category_capacities for value in agent_category_capacities[agent].values() if value < 0] From 79aed16b1ebbc08aeeaa0a962d0681a1fdd31ef6 Mon Sep 17 00:00:00 2001 From: Abodi Massarwa Date: Mon, 5 Aug 2024 20:07:05 +0300 Subject: [PATCH 04/36] fixed bug in helper_categorization_friendly_picking_sequence(...) in heterogeneous_matroid_constraints_algorithms.py --- ...ogeneous_matroid_constraints_algorithms.py | 82 ++++++++++++------- 1 file changed, 53 insertions(+), 29 deletions(-) diff --git a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py index 7638d04..b9375f0 100644 --- a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py +++ b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py @@ -635,23 +635,36 @@ def helper_categorization_friendly_picking_sequence(alloc:AllocationBuilder, age logger.info(f'remaining_category_items -> {remaining_category_items} & remaining agent capacities {remaining_category_agent_capacities}') logger.info(f"Agent order is -> {agent_order}") remaining_agents_with_capacities = {agent for agent,capacity in remaining_category_agent_capacities.items() if capacity>0}# all the agents with non zero capacities in our category - for agent in cycle(agent_order): + for agent in cycle(agent_order):# agent order cant be changed , it would still consist of expired agents but we're handling it in our own ways logger.info("Looping agent %s, remaining capacity %s", agent, remaining_category_agent_capacities[agent]) + if agent not in remaining_agents_with_capacities or not remaining_agents_with_capacities: # means no agents left that are feasible to get items + if not remaining_agents_with_capacities: + logger.info(f'No agents left due to either:\n 1) reached maximum capacity\n 2) already has copy of item and cant carry more than 1 copy \n breaking out of loop!') + break + else: # means only pass to other agent and there is still agents to carry items + continue + + if remaining_category_agent_capacities[agent] <= 0: remaining_agents_with_capacities.discard(agent) + logger.info(f'{agent} removed from loop since he has no capacity!') if len(remaining_agents_with_capacities) == 0: logger.info(f'No more agents with capacity') break continue potential_items_for_agent = set(remaining_category_items).difference(alloc.bundles[agent]) # in case difference is empty means already has a duplicate of the item(legal) / there is no items left + logger.info(f'potential set of items to be allocated to {agent} are -> {potential_items_for_agent}') if len(potential_items_for_agent) == 0: # still has capacity, but no items to aquire (maybe no items left maybe already has copy of item) logger.info(f'No potential items for agent {agent}') + logger.info(f'remaining_agents_with_capacities is -> {remaining_agents_with_capacities},agent order is -> {agent_order}') if agent in remaining_agents_with_capacities: # need to remove agent from our loop ,even if he still has capacity ! + logger.info(f'{agent} still has capacity but already has copy of the item') #del remaining_category_agent_capacities[agent] remaining_agents_with_capacities.discard(agent) + logger.info(f'{agent} removed from loop') if len(remaining_agents_with_capacities) == 0: - logger.info(f'No more agents with capacity') + logger.info(f'No more agents with capacity,breaking loop!') break continue # otherwise pick the next agent ! # safe to assume agent has capacity & has the best item to pick @@ -1192,34 +1205,45 @@ def helper_validate_item_categories(item_categories:dict[str, list]): if __name__ == "__main__": import doctest, sys - print("\n", doctest.testmod(), "\n") + #print("\n", doctest.testmod(), "\n") logger.setLevel(logging.DEBUG) logger.addHandler(logging.StreamHandler()) - order=['Agent1','Agent2','Agent3','Agent4'] - items=['m1','m2','m3','m4'] - item_categories = {'c1': ['m1', 'm2','m3'],'c2':['m4']} - agent_category_capacities = {'Agent1': {'c1':3,'c2':2}, 'Agent2': {'c1':3,'c2':2},'Agent3': {'c1':3,'c2':2},'Agent4': {'c1':3,'c2':2}} # in the papers its written capacity=size(catergory) - valuations = {'Agent1':{'m1':2,'m2':1,'m3':1,'m4':10},'Agent2':{'m1':1,'m2':2,'m3':1,'m4':10},'Agent3':{'m1':1,'m2':1,'m3':2,'m4':10},'Agent4':{'m1':1,'m2':1,'m3':1,'m4':10}} - sum_agent_category_capacities={agent:sum(cap.values()) for agent,cap in agent_category_capacities.items()} - instance=Instance(valuations=valuations,items=items,agent_capacities=sum_agent_category_capacities) - divide(algorithm=per_category_round_robin,instance=instance,item_categories=item_categories,agent_category_capacities=agent_category_capacities,initial_agent_order=order) - divide(algorithm=two_categories_capped_round_robin,instance=instance,item_categories=item_categories,agent_category_capacities=agent_category_capacities,initial_agent_order=order,target_category_pair=("c1","c2")) - - items=['m1','m2','m3'] - item_categories = {'c1': ['m1'],'c2':['m2','m3']} - agent_category_capacities = {'Agent1': {'c1':2,'c2':2}, 'Agent2': {'c1':2,'c2':2},'Agent3': {'c1':2,'c2':2}} - valuations = {'Agent1':{'m1':1,'m2':1,'m3':1},'Agent2':{'m1':1,'m2':1,'m3':0},'Agent3':{'m1':0,'m2':0,'m3':0}} - instance=Instance(valuations=valuations,items=items,agent_capacities=sum_agent_category_capacities) - divide(algorithm=iterated_priority_matching,instance=instance,item_categories=item_categories,agent_category_capacities=agent_category_capacities) - - order = ['Agent1', 'Agent2'] - items = ['m1'] - item_categories = {'c1': ['m1']} - agent_category_capacities = {'Agent1': {'c1': 0}, 'Agent2': {'c1': 1}} - valuations = {'Agent1': {'m1': 0}, 'Agent2': {'m1': 420}} - target_category = 'c1' - divide(algorithm=capped_round_robin, instance=Instance(valuations=valuations, items=items), - item_categories=item_categories, agent_category_capacities=agent_category_capacities, - initial_agent_order=order, target_category=target_category) + # order=['Agent1','Agent2','Agent3','Agent4'] + # items=['m1','m2','m3','m4'] + # item_categories = {'c1': ['m1', 'm2','m3'],'c2':['m4']} + # agent_category_capacities = {'Agent1': {'c1':3,'c2':2}, 'Agent2': {'c1':3,'c2':2},'Agent3': {'c1':3,'c2':2},'Agent4': {'c1':3,'c2':2}} # in the papers its written capacity=size(catergory) + # valuations = {'Agent1':{'m1':2,'m2':1,'m3':1,'m4':10},'Agent2':{'m1':1,'m2':2,'m3':1,'m4':10},'Agent3':{'m1':1,'m2':1,'m3':2,'m4':10},'Agent4':{'m1':1,'m2':1,'m3':1,'m4':10}} + # sum_agent_category_capacities={agent:sum(cap.values()) for agent,cap in agent_category_capacities.items()} + # instance=Instance(valuations=valuations,items=items,agent_capacities=sum_agent_category_capacities) + # divide(algorithm=per_category_round_robin,instance=instance,item_categories=item_categories,agent_category_capacities=agent_category_capacities,initial_agent_order=order) + # divide(algorithm=two_categories_capped_round_robin,instance=instance,item_categories=item_categories,agent_category_capacities=agent_category_capacities,initial_agent_order=order,target_category_pair=("c1","c2")) + # + # items=['m1','m2','m3'] + # item_categories = {'c1': ['m1'],'c2':['m2','m3']} + # agent_category_capacities = {'Agent1': {'c1':2,'c2':2}, 'Agent2': {'c1':2,'c2':2},'Agent3': {'c1':2,'c2':2}} + # valuations = {'Agent1':{'m1':1,'m2':1,'m3':1},'Agent2':{'m1':1,'m2':1,'m3':0},'Agent3':{'m1':0,'m2':0,'m3':0}} + # instance=Instance(valuations=valuations,items=items,agent_capacities=sum_agent_category_capacities) + # divide(algorithm=iterated_priority_matching,instance=instance,item_categories=item_categories,agent_category_capacities=agent_category_capacities) + # + # order = ['Agent1', 'Agent2'] + # items = ['m1'] + # item_categories = {'c1': ['m1']} + # agent_category_capacities = {'Agent1': {'c1': 0}, 'Agent2': {'c1': 1}} + # valuations = {'Agent1': {'m1': 0}, 'Agent2': {'m1': 420}} + # target_category = 'c1' + # divide(algorithm=capped_round_robin, instance=Instance(valuations=valuations, items=items), + # item_categories=item_categories, agent_category_capacities=agent_category_capacities, + # initial_agent_order=order, target_category=target_category) + item_categories={'category_1':['item_1'],'category_2':['item_2']} + item_capacities={'item_1':100,'item_2':500} + agent_category_capacities={'agent_1':{'category_1':5,'category_2':5},'agent_2':{'category_1':5,'category_2':5}} + item_valuations={'agent_1':{'item_1':0,'item_2':1},'agent_2':{'item_1':1,'item_2':0}} + items=['item_1','item_2'] + divide(algorithm=iterated_priority_matching, instance=Instance(valuations=item_valuations, item_capacities=item_capacities,items=items), + item_categories=item_categories, agent_category_capacities=agent_category_capacities, + ) + + + From eba114bf443ef322b95c76a7c3216fca6535f6b4 Mon Sep 17 00:00:00 2001 From: Abodi Massarwa Date: Mon, 5 Aug 2024 20:38:31 +0300 Subject: [PATCH 05/36] fixed bug in helper_categorization_friendly_picking_sequence(...) in heterogeneous_matroid_constraints_algorithms.py --- .../heterogeneous_matroid_constraints_algorithms.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py index b9375f0..01e91c3 100644 --- a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py +++ b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py @@ -434,7 +434,8 @@ def iterated_priority_matching(alloc: AllocationBuilder, item_categories: dict[s agents_with_remaining_capacities = [agent for agent,capacity in remaining_category_agent_capacities.items() if capacity>0] logger.info(f'remaining_category_agent_capacities of agents capable of carrying arbitrary item ->{remaining_category_agent_capacities}') logger.info(f'Using round-robin to allocate the items that were not allocated in the priority matching ->{remaining_category_items}') - helper_categorization_friendly_picking_sequence(alloc, agents_with_remaining_capacities, item_categories[category], agent_category_capacities={agent:{category:remaining_category_agent_capacities[agent]} for agent in remaining_category_agent_capacities.keys()}, target_category=category) + if remaining_category_items and remaining_category_agent_capacities: + helper_categorization_friendly_picking_sequence(alloc, agents_with_remaining_capacities, item_categories[category], agent_category_capacities={agent:{category:remaining_category_agent_capacities[agent]} for agent in remaining_category_agent_capacities.keys()}, target_category=category) logger.info(f'FINAL ALLOCATION IS -> {alloc.bundles}') @@ -621,6 +622,7 @@ def helper_categorization_friendly_picking_sequence(alloc:AllocationBuilder, age if not isinstance(target_category, str): raise ValueError("target_category must be of type str!") categories=list(set([category for agent,dict in agent_category_capacities.items() for category in dict.keys()])) + logger.info(f"target category is ->{target_category} ,agent_category_capacities are -> {agent_category_capacities}") if target_category not in categories: raise ValueError(f"Target category mistyped or not found: {target_category}") @@ -1237,8 +1239,8 @@ def helper_validate_item_categories(item_categories:dict[str, list]): # item_categories=item_categories, agent_category_capacities=agent_category_capacities, # initial_agent_order=order, target_category=target_category) item_categories={'category_1':['item_1'],'category_2':['item_2']} - item_capacities={'item_1':100,'item_2':500} - agent_category_capacities={'agent_1':{'category_1':5,'category_2':5},'agent_2':{'category_1':5,'category_2':5}} + item_capacities={'item_1':2,'item_2':5} + agent_category_capacities={'agent_1':{'category_1':5,'category_2':1},'agent_2':{'category_1':1,'category_2':0}} item_valuations={'agent_1':{'item_1':0,'item_2':1},'agent_2':{'item_1':1,'item_2':0}} items=['item_1','item_2'] divide(algorithm=iterated_priority_matching, instance=Instance(valuations=item_valuations, item_capacities=item_capacities,items=items), From 9d550b7fe4b3f5cdff4ddb86cc4989f81f13c146 Mon Sep 17 00:00:00 2001 From: Abodi Massarwa Date: Tue, 6 Aug 2024 05:07:44 +0300 Subject: [PATCH 06/36] added functionality to return image_base64 to our flask_app --- ...ogeneous_matroid_constraints_algorithms.py | 52 ++++++++++++++++--- 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py index 01e91c3..a094610 100644 --- a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py +++ b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py @@ -16,11 +16,14 @@ import networkx as nx import logging import numpy as np +import matplotlib.pyplot as plt +import io +import base64 logger = logging.getLogger(__name__) def per_category_round_robin(alloc: AllocationBuilder, item_categories: dict[str,list], agent_category_capacities: dict[str,dict[str,int]], - initial_agent_order: list): + initial_agent_order: list,callback:callable=None): """ this is the Algorithm 1 from the paper per category round-robin is an allocation algorithm which guarantees EF1 (envy-freeness up to 1 good) allocation @@ -83,11 +86,11 @@ def per_category_round_robin(alloc: AllocationBuilder, item_categories: dict[str logger.info(f'\nCurrent category -> {category}') logger.info(f'Envy graph before RR -> {envy_graph.nodes}, edges -> in {envy_graph.edges}') helper_categorization_friendly_picking_sequence(alloc, current_order, item_categories[category], agent_category_capacities, category) - helper_update_envy_graph(alloc.bundles, valuation_func, envy_graph, item_categories, agent_category_capacities) + helper_update_envy_graph(alloc.bundles, valuation_func, envy_graph, item_categories, agent_category_capacities,callback) logger.info(f'Envy graph after RR -> {envy_graph.nodes}, edges -> in {envy_graph.edges}') if not nx.is_directed_acyclic_graph(envy_graph): logger.info("Cycle removal started ") - helper_remove_cycles(envy_graph, alloc, valuation_func, item_categories, agent_category_capacities) + helper_remove_cycles(envy_graph, alloc, valuation_func, item_categories, agent_category_capacities,callback) logger.info('cycle removal ended successfully ') current_order = list(nx.topological_sort(envy_graph)) logger.info(f"Topological sort -> {current_order} \n***************************** ") @@ -329,7 +332,7 @@ def per_category_capped_round_robin(alloc: AllocationBuilder,item_categories: di logger.info(f'allocation after termination of algorithm4 -> {alloc.bundles}') -def iterated_priority_matching(alloc: AllocationBuilder, item_categories: dict[str,list], agent_category_capacities: dict[str,dict[str,int]]): +def iterated_priority_matching(alloc: AllocationBuilder, item_categories: dict[str,list], agent_category_capacities: dict[str,dict[str,int]],callback:callable=None): """ this is Algorithm 5 deals with (partition Matroids with Binary Valuations, may have different capacities) loops as much as maximum capacity in per each category , each iteration we build : @@ -411,11 +414,14 @@ def iterated_priority_matching(alloc: AllocationBuilder, item_categories: dict[s valuation_func=valuation_func, # remaining agents with respect to the order ) # building the Bi-Partite graph + if callback: + img_base64=helper_generate_graph_base64(agent_item_bipartite_graph) + callback(img_base64) # Creation of envy graph helper_update_envy_graph(curr_bundles=alloc.bundles, valuation_func=valuation_func, envy_graph=envy_graph, item_categories=item_categories, - agent_category_capacities=agent_category_capacities) # updating envy graph with respect to matchings (first iteration we get no envy, cause there is no matching) + agent_category_capacities=agent_category_capacities,callback=callback) # updating envy graph with respect to matchings (first iteration we get no envy, cause there is no matching) #topological sort (papers prove graph is always a-cyclic) topological_sort = list(nx.topological_sort(envy_graph)) logger.info(f'topological sort is -> {topological_sort}') @@ -683,7 +689,7 @@ def helper_categorization_friendly_picking_sequence(alloc:AllocationBuilder, age def helper_update_envy_graph(curr_bundles: dict, valuation_func: callable, envy_graph: DiGraph, item_categories: dict[str,list], - agent_category_capacities: dict[str,dict[str,int]]): + agent_category_capacities: dict[str,dict[str,int]],callback:callable=None): """ simply a helper function to update the envy-graph based on given params :param curr_bundles: the current allocation @@ -759,6 +765,8 @@ def helper_update_envy_graph(curr_bundles: dict, valuation_func: callable, envy_ #print(f"{agent1} envies {agent2}") # works great . # we need to add edge from the envier to the envyee envy_graph.add_edge(agent1, agent2) + if callback: + callback(envy_graph) logger.info(f"envy_graph.edges after update -> {envy_graph.edges}") # def visualize_graph(envy_graph): @@ -769,7 +777,7 @@ def helper_update_envy_graph(curr_bundles: dict, valuation_func: callable, envy_ # plt.show() # -def helper_remove_cycles(envy_graph:nx.DiGraph, alloc:AllocationBuilder, valuation_func:callable, item_categories:dict[str,list], agent_category_capacities:dict[str,dict[str,int]]): +def helper_remove_cycles(envy_graph:nx.DiGraph, alloc:AllocationBuilder, valuation_func:callable, item_categories:dict[str,list], agent_category_capacities:dict[str,dict[str,int]],callback:callable=None): """ Removes cycles from the envy graph by updating the bundles. @@ -893,6 +901,11 @@ def helper_remove_cycles(envy_graph:nx.DiGraph, alloc:AllocationBuilder, valuati # Update the envy graph after swapping helper_update_envy_graph(alloc.bundles, valuation_func, envy_graph, item_categories, agent_category_capacities) + #callback section for our flask_app + if callback: + img_base64=helper_generate_graph_base64(envy_graph) + callback(img_base64) + logger.info(f"Updated envy graph. is Graph acyclic ?? {nx.is_directed_acyclic_graph(envy_graph)}") except nx.NetworkXNoCycle: @@ -1205,6 +1218,31 @@ def helper_validate_item_categories(item_categories:dict[str, list]): else: raise ValueError(f"item categories is supposed to be dict[str,list] but u entered {type(item_categories)}") + +def helper_generate_graph_base64(graph): + plt.figure() + + if nx.is_bipartite(graph): + # If the graph is bipartite, use the bipartite layout + top_nodes, bottom_nodes = nx.bipartite.sets(graph) + pos = nx.bipartite_layout(graph, top_nodes) + else: + # For other types of graphs, use the spring layout + pos = nx.spring_layout(graph) + + # Draw the graph + nx.draw(graph, pos, with_labels=True, node_color='lightblue', edge_color='gray', node_size=500, font_size=10) + + # Save the graph to a BytesIO object + img_bytes = io.BytesIO() + plt.savefig(img_bytes, format='png') + plt.close() + img_bytes.seek(0) + + # Return the base64-encoded image + return base64.b64encode(img_bytes.read()).decode('utf-8') + + if __name__ == "__main__": import doctest, sys #print("\n", doctest.testmod(), "\n") From 1d663d60a4cb8995a0665782b1aba0fe7d57e362 Mon Sep 17 00:00:00 2001 From: Abodi Massarwa Date: Tue, 6 Aug 2024 05:24:53 +0300 Subject: [PATCH 07/36] slight modifications --- .../heterogeneous_matroid_constraints_algorithms.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py index a094610..b7ab5dc 100644 --- a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py +++ b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py @@ -415,8 +415,7 @@ def iterated_priority_matching(alloc: AllocationBuilder, item_categories: dict[s # remaining agents with respect to the order ) # building the Bi-Partite graph if callback: - img_base64=helper_generate_graph_base64(agent_item_bipartite_graph) - callback(img_base64) + callback(helper_generate_graph_base64(envy_graph)) # Creation of envy graph helper_update_envy_graph(curr_bundles=alloc.bundles, valuation_func=valuation_func, envy_graph=envy_graph, @@ -766,7 +765,7 @@ def helper_update_envy_graph(curr_bundles: dict, valuation_func: callable, envy_ # we need to add edge from the envier to the envyee envy_graph.add_edge(agent1, agent2) if callback: - callback(envy_graph) + callback(helper_generate_graph_base64(envy_graph)) logger.info(f"envy_graph.edges after update -> {envy_graph.edges}") # def visualize_graph(envy_graph): @@ -903,8 +902,7 @@ def helper_remove_cycles(envy_graph:nx.DiGraph, alloc:AllocationBuilder, valuati helper_update_envy_graph(alloc.bundles, valuation_func, envy_graph, item_categories, agent_category_capacities) #callback section for our flask_app if callback: - img_base64=helper_generate_graph_base64(envy_graph) - callback(img_base64) + callback(helper_generate_graph_base64(envy_graph)) logger.info(f"Updated envy graph. is Graph acyclic ?? {nx.is_directed_acyclic_graph(envy_graph)}") From 6e0b0df4b11fcaa85cf11933f945b96eef03adff Mon Sep 17 00:00:00 2001 From: Abodi Massarwa Date: Tue, 6 Aug 2024 05:32:17 +0300 Subject: [PATCH 08/36] slight modifications --- ...rogeneous_matroid_constraints_algorithms.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py index b7ab5dc..e3f3fbf 100644 --- a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py +++ b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py @@ -1220,24 +1220,26 @@ def helper_validate_item_categories(item_categories:dict[str, list]): def helper_generate_graph_base64(graph): plt.figure() - if nx.is_bipartite(graph): - # If the graph is bipartite, use the bipartite layout - top_nodes, bottom_nodes = nx.bipartite.sets(graph) - pos = nx.bipartite_layout(graph, top_nodes) - else: - # For other types of graphs, use the spring layout + try: + if nx.is_bipartite(graph): + # If the graph is bipartite, use the bipartite layout + top_nodes, bottom_nodes = nx.bipartite.sets(graph) + pos = nx.bipartite_layout(graph, top_nodes) + else: + # For other types of graphs, use the spring layout + pos = nx.spring_layout(graph) + except nx.NetworkXError as e: + # Handle the case where the graph is disconnected pos = nx.spring_layout(graph) # Draw the graph nx.draw(graph, pos, with_labels=True, node_color='lightblue', edge_color='gray', node_size=500, font_size=10) - # Save the graph to a BytesIO object img_bytes = io.BytesIO() plt.savefig(img_bytes, format='png') plt.close() img_bytes.seek(0) - # Return the base64-encoded image return base64.b64encode(img_bytes.read()).decode('utf-8') From b67695db8576f0689fdf566cd1c8a1f9dc35b17f Mon Sep 17 00:00:00 2001 From: Abodi Massarwa Date: Tue, 6 Aug 2024 05:40:04 +0300 Subject: [PATCH 09/36] slight modifications --- .../algorithms/heterogeneous_matroid_constraints_algorithms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py index e3f3fbf..ca53021 100644 --- a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py +++ b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py @@ -415,7 +415,7 @@ def iterated_priority_matching(alloc: AllocationBuilder, item_categories: dict[s # remaining agents with respect to the order ) # building the Bi-Partite graph if callback: - callback(helper_generate_graph_base64(envy_graph)) + callback(helper_generate_graph_base64(agent_item_bipartite_graph)) # Creation of envy graph helper_update_envy_graph(curr_bundles=alloc.bundles, valuation_func=valuation_func, envy_graph=envy_graph, From fe4f6cd1d6b33ad5747ca2e2a5a195bc28d76469 Mon Sep 17 00:00:00 2001 From: Abodi Massarwa Date: Tue, 6 Aug 2024 05:52:05 +0300 Subject: [PATCH 10/36] slight modifications --- ...ogeneous_matroid_constraints_algorithms.py | 37 +++++++++++++------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py index ca53021..8a5ff73 100644 --- a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py +++ b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py @@ -415,7 +415,7 @@ def iterated_priority_matching(alloc: AllocationBuilder, item_categories: dict[s # remaining agents with respect to the order ) # building the Bi-Partite graph if callback: - callback(helper_generate_graph_base64(agent_item_bipartite_graph)) + callback(helper_generate_bipartite_graph_base64(agent_item_bipartite_graph)) # Creation of envy graph helper_update_envy_graph(curr_bundles=alloc.bundles, valuation_func=valuation_func, envy_graph=envy_graph, @@ -1217,22 +1217,35 @@ def helper_validate_item_categories(item_categories:dict[str, list]): raise ValueError(f"item categories is supposed to be dict[str,list] but u entered {type(item_categories)}") -def helper_generate_graph_base64(graph): +def helper_generate_directed_graph_base64(graph): plt.figure() + pos = nx.spring_layout(graph) + nx.draw(graph, pos, with_labels=True, node_color='lightblue', edge_color='gray', node_size=500, font_size=10, + arrows=True) + img_bytes = io.BytesIO() + plt.savefig(img_bytes, format='png') + plt.close() + img_bytes.seek(0) + + return base64.b64encode(img_bytes.read()).decode('utf-8') + + +def helper_generate_bipartite_graph_base64(graph): + plt.figure() try: - if nx.is_bipartite(graph): - # If the graph is bipartite, use the bipartite layout - top_nodes, bottom_nodes = nx.bipartite.sets(graph) - pos = nx.bipartite_layout(graph, top_nodes) - else: - # For other types of graphs, use the spring layout - pos = nx.spring_layout(graph) - except nx.NetworkXError as e: - # Handle the case where the graph is disconnected + # Get all connected components of the graph + components = nx.connected_components(graph) + pos = {} + for component in components: + subgraph = graph.subgraph(component) + top_nodes, bottom_nodes = nx.bipartite.sets(subgraph) + component_pos = nx.bipartite_layout(subgraph, top_nodes) + pos.update(component_pos) + except nx.NetworkXError: + # Fallback to spring layout if there's an error pos = nx.spring_layout(graph) - # Draw the graph nx.draw(graph, pos, with_labels=True, node_color='lightblue', edge_color='gray', node_size=500, font_size=10) img_bytes = io.BytesIO() From e86c0c0ad5f2def4bf35298e36b7514a12d1ea3f Mon Sep 17 00:00:00 2001 From: Abodi Massarwa Date: Tue, 6 Aug 2024 06:10:37 +0300 Subject: [PATCH 11/36] slight modifications --- ...ogeneous_matroid_constraints_algorithms.py | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py index 8a5ff73..bf6568a 100644 --- a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py +++ b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py @@ -1219,6 +1219,7 @@ def helper_validate_item_categories(item_categories:dict[str, list]): def helper_generate_directed_graph_base64(graph): plt.figure() + plt.title('Envy Graph') pos = nx.spring_layout(graph) nx.draw(graph, pos, with_labels=True, node_color='lightblue', edge_color='gray', node_size=500, font_size=10, arrows=True) @@ -1233,20 +1234,26 @@ def helper_generate_directed_graph_base64(graph): def helper_generate_bipartite_graph_base64(graph): plt.figure() + plt.title('Agent-Item Bipartite Graph', fontsize=16) try: - # Get all connected components of the graph - components = nx.connected_components(graph) - pos = {} - for component in components: - subgraph = graph.subgraph(component) - top_nodes, bottom_nodes = nx.bipartite.sets(subgraph) - component_pos = nx.bipartite_layout(subgraph, top_nodes) - pos.update(component_pos) + # Use spring layout for better aesthetics + pos = nx.spring_layout(graph, k=0.5) # Adjust k for different spacing + color_map = [] + for node in graph.nodes: + if graph.nodes[node].get('bipartite') == 0: + color_map.append('red') + else: + color_map.append('blue') except nx.NetworkXError: # Fallback to spring layout if there's an error pos = nx.spring_layout(graph) + # Assign default colors if layout falls back + color_map = ['red' for node in graph.nodes] - nx.draw(graph, pos, with_labels=True, node_color='lightblue', edge_color='gray', node_size=500, font_size=10) + nx.draw(graph, pos, with_labels=True, node_color=color_map, edge_color='gray', node_size=500, font_size=10) + + # Adjust the layout to make sure the title is visible + plt.tight_layout(rect=[0, 0, 1, 0.95]) img_bytes = io.BytesIO() plt.savefig(img_bytes, format='png') From b3b4ac7d614387bc306d9f5990d2547a0193b430 Mon Sep 17 00:00:00 2001 From: Abodi Massarwa Date: Tue, 6 Aug 2024 06:14:25 +0300 Subject: [PATCH 12/36] slight modifications --- ...rogeneous_matroid_constraints_algorithms.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py index bf6568a..e605c6a 100644 --- a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py +++ b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py @@ -1236,14 +1236,16 @@ def helper_generate_bipartite_graph_base64(graph): plt.figure() plt.title('Agent-Item Bipartite Graph', fontsize=16) try: - # Use spring layout for better aesthetics - pos = nx.spring_layout(graph, k=0.5) # Adjust k for different spacing - color_map = [] - for node in graph.nodes: - if graph.nodes[node].get('bipartite') == 0: - color_map.append('red') - else: - color_map.append('blue') + top_nodes = {n for n, d in graph.nodes(data=True) if d['bipartite'] == 0} + bottom_nodes = set(graph) - top_nodes + + # Create fixed positions + pos = {} + pos.update((node, (1, index)) for index, node in enumerate(top_nodes)) # x=1 for top_nodes + pos.update((node, (2, index)) for index, node in enumerate(bottom_nodes)) # x=2 for bottom_nodes + + # Assign colors to the nodes based on their group + color_map = ['red' if node in top_nodes else 'blue' for node in graph.nodes] except nx.NetworkXError: # Fallback to spring layout if there's an error pos = nx.spring_layout(graph) From 8bfc310bbd01d4fef43ab2028323ec2f4afb0889 Mon Sep 17 00:00:00 2001 From: Abodi Massarwa Date: Tue, 6 Aug 2024 06:23:59 +0300 Subject: [PATCH 13/36] slight modifications --- .../heterogeneous_matroid_constraints_algorithms.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py index e605c6a..1dad6c3 100644 --- a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py +++ b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py @@ -415,7 +415,7 @@ def iterated_priority_matching(alloc: AllocationBuilder, item_categories: dict[s # remaining agents with respect to the order ) # building the Bi-Partite graph if callback: - callback(helper_generate_bipartite_graph_base64(agent_item_bipartite_graph)) + callback(helper_generate_bipartite_graph_base64(agent_item_bipartite_graph,iteration=i, category=category)) # Creation of envy graph helper_update_envy_graph(curr_bundles=alloc.bundles, valuation_func=valuation_func, envy_graph=envy_graph, @@ -1232,9 +1232,11 @@ def helper_generate_directed_graph_base64(graph): return base64.b64encode(img_bytes.read()).decode('utf-8') -def helper_generate_bipartite_graph_base64(graph): +def helper_generate_bipartite_graph_base64(graph,iteration:int,category:str): plt.figure() plt.title('Agent-Item Bipartite Graph', fontsize=16) + additional_text=f'{category} iteration {iteration}' + plt.figtext(0.5, 0.95, additional_text, wrap=True, horizontalalignment='center', fontsize=10) try: top_nodes = {n for n, d in graph.nodes(data=True) if d['bipartite'] == 0} bottom_nodes = set(graph) - top_nodes @@ -1255,7 +1257,7 @@ def helper_generate_bipartite_graph_base64(graph): nx.draw(graph, pos, with_labels=True, node_color=color_map, edge_color='gray', node_size=500, font_size=10) # Adjust the layout to make sure the title is visible - plt.tight_layout(rect=[0, 0, 1, 0.95]) + plt.tight_layout(rect=[0, 0, 1, 0.90]) img_bytes = io.BytesIO() plt.savefig(img_bytes, format='png') From ab40f1dff19dd7c8c51744c7b6d41d2cf98cb833 Mon Sep 17 00:00:00 2001 From: Abodi Massarwa Date: Tue, 6 Aug 2024 06:32:02 +0300 Subject: [PATCH 14/36] slight modifications --- ...eterogeneous_matroid_constraints_algorithms.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py index 1dad6c3..6d9389c 100644 --- a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py +++ b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py @@ -765,7 +765,7 @@ def helper_update_envy_graph(curr_bundles: dict, valuation_func: callable, envy_ # we need to add edge from the envier to the envyee envy_graph.add_edge(agent1, agent2) if callback: - callback(helper_generate_graph_base64(envy_graph)) + callback(helper_generate_directed_graph_base64(envy_graph)) logger.info(f"envy_graph.edges after update -> {envy_graph.edges}") # def visualize_graph(envy_graph): @@ -902,7 +902,7 @@ def helper_remove_cycles(envy_graph:nx.DiGraph, alloc:AllocationBuilder, valuati helper_update_envy_graph(alloc.bundles, valuation_func, envy_graph, item_categories, agent_category_capacities) #callback section for our flask_app if callback: - callback(helper_generate_graph_base64(envy_graph)) + callback(helper_generate_directed_graph_base64(envy_graph)) logger.info(f"Updated envy graph. is Graph acyclic ?? {nx.is_directed_acyclic_graph(envy_graph)}") @@ -1217,19 +1217,20 @@ def helper_validate_item_categories(item_categories:dict[str, list]): raise ValueError(f"item categories is supposed to be dict[str,list] but u entered {type(item_categories)}") -def helper_generate_directed_graph_base64(graph): +def helper_generate_directed_graph_base64(graph, seed=42): plt.figure() plt.title('Envy Graph') - pos = nx.spring_layout(graph) - nx.draw(graph, pos, with_labels=True, node_color='lightblue', edge_color='gray', node_size=500, font_size=10, - arrows=True) + pos = nx.spring_layout(graph, seed=seed) # Use a seed for reproducibility + nx.draw(graph, pos, with_labels=True, node_color='lightblue', edge_color='gray', node_size=500, font_size=10, arrows=True) img_bytes = io.BytesIO() plt.savefig(img_bytes, format='png') plt.close() img_bytes.seek(0) - return base64.b64encode(img_bytes.read()).decode('utf-8') + base64_image = base64.b64encode(img_bytes.read()).decode('utf-8') + print("Generated image data:", base64_image[:100]) # Print the first 100 characters of the image data + return base64_image def helper_generate_bipartite_graph_base64(graph,iteration:int,category:str): From 9ef29e7bd2af4eaf04ea360ce6805f95197ea796 Mon Sep 17 00:00:00 2001 From: Abodi Massarwa Date: Tue, 6 Aug 2024 06:37:10 +0300 Subject: [PATCH 15/36] minor modifications for the sake of testing --- .../algorithms/heterogeneous_matroid_constraints_algorithms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py index 6d9389c..84e293f 100644 --- a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py +++ b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py @@ -1230,7 +1230,7 @@ def helper_generate_directed_graph_base64(graph, seed=42): base64_image = base64.b64encode(img_bytes.read()).decode('utf-8') print("Generated image data:", base64_image[:100]) # Print the first 100 characters of the image data - return base64_image + return 'base64_image' def helper_generate_bipartite_graph_base64(graph,iteration:int,category:str): From 3229247a65e96e43bfb70bb409b797e38a9f2026 Mon Sep 17 00:00:00 2001 From: Abodi Massarwa Date: Tue, 6 Aug 2024 06:57:04 +0300 Subject: [PATCH 16/36] minor modifications for the sake of testing --- ...ogeneous_matroid_constraints_algorithms.py | 57 +++++++++---------- 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py index 84e293f..2f37046 100644 --- a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py +++ b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py @@ -1218,18 +1218,18 @@ def helper_validate_item_categories(item_categories:dict[str, list]): def helper_generate_directed_graph_base64(graph, seed=42): - plt.figure() - plt.title('Envy Graph') - pos = nx.spring_layout(graph, seed=seed) # Use a seed for reproducibility - nx.draw(graph, pos, with_labels=True, node_color='lightblue', edge_color='gray', node_size=500, font_size=10, arrows=True) - - img_bytes = io.BytesIO() - plt.savefig(img_bytes, format='png') - plt.close() - img_bytes.seek(0) - - base64_image = base64.b64encode(img_bytes.read()).decode('utf-8') - print("Generated image data:", base64_image[:100]) # Print the first 100 characters of the image data + # plt.figure() + # plt.title('Envy Graph') + # pos = nx.spring_layout(graph, seed=seed) # Use a seed for reproducibility + # nx.draw(graph, pos, with_labels=True, node_color='lightblue', edge_color='gray', node_size=500, font_size=10, arrows=True) + # + # img_bytes = io.BytesIO() + # plt.savefig(img_bytes, format='png') + # plt.close() + # img_bytes.seek(0) + # + # base64_image = base64.b64encode(img_bytes.read()).decode('utf-8') + # print("Generated image data:", base64_image[:100]) # Print the first 100 characters of the image data return 'base64_image' @@ -1267,7 +1267,6 @@ def helper_generate_bipartite_graph_base64(graph,iteration:int,category:str): return base64.b64encode(img_bytes.read()).decode('utf-8') - if __name__ == "__main__": import doctest, sys #print("\n", doctest.testmod(), "\n") @@ -1275,14 +1274,14 @@ def helper_generate_bipartite_graph_base64(graph,iteration:int,category:str): logger.setLevel(logging.DEBUG) logger.addHandler(logging.StreamHandler()) - # order=['Agent1','Agent2','Agent3','Agent4'] - # items=['m1','m2','m3','m4'] - # item_categories = {'c1': ['m1', 'm2','m3'],'c2':['m4']} - # agent_category_capacities = {'Agent1': {'c1':3,'c2':2}, 'Agent2': {'c1':3,'c2':2},'Agent3': {'c1':3,'c2':2},'Agent4': {'c1':3,'c2':2}} # in the papers its written capacity=size(catergory) - # valuations = {'Agent1':{'m1':2,'m2':1,'m3':1,'m4':10},'Agent2':{'m1':1,'m2':2,'m3':1,'m4':10},'Agent3':{'m1':1,'m2':1,'m3':2,'m4':10},'Agent4':{'m1':1,'m2':1,'m3':1,'m4':10}} - # sum_agent_category_capacities={agent:sum(cap.values()) for agent,cap in agent_category_capacities.items()} - # instance=Instance(valuations=valuations,items=items,agent_capacities=sum_agent_category_capacities) - # divide(algorithm=per_category_round_robin,instance=instance,item_categories=item_categories,agent_category_capacities=agent_category_capacities,initial_agent_order=order) + order=['Agent1','Agent2','Agent3','Agent4'] + items=['m1','m2','m3','m4'] + item_categories = {'c1': ['m1', 'm2','m3'],'c2':['m4']} + agent_category_capacities = {'Agent1': {'c1':3,'c2':2}, 'Agent2': {'c1':3,'c2':2},'Agent3': {'c1':3,'c2':2},'Agent4': {'c1':3,'c2':2}} # in the papers its written capacity=size(catergory) + valuations = {'Agent1':{'m1':2,'m2':1,'m3':1,'m4':10},'Agent2':{'m1':1,'m2':2,'m3':1,'m4':10},'Agent3':{'m1':1,'m2':1,'m3':2,'m4':10},'Agent4':{'m1':1,'m2':1,'m3':1,'m4':10}} + sum_agent_category_capacities={agent:sum(cap.values()) for agent,cap in agent_category_capacities.items()} + instance=Instance(valuations=valuations,items=items,agent_capacities=sum_agent_category_capacities) + divide(algorithm=per_category_round_robin,instance=instance,item_categories=item_categories,agent_category_capacities=agent_category_capacities,initial_agent_order=order) # divide(algorithm=two_categories_capped_round_robin,instance=instance,item_categories=item_categories,agent_category_capacities=agent_category_capacities,initial_agent_order=order,target_category_pair=("c1","c2")) # # items=['m1','m2','m3'] @@ -1301,14 +1300,14 @@ def helper_generate_bipartite_graph_base64(graph,iteration:int,category:str): # divide(algorithm=capped_round_robin, instance=Instance(valuations=valuations, items=items), # item_categories=item_categories, agent_category_capacities=agent_category_capacities, # initial_agent_order=order, target_category=target_category) - item_categories={'category_1':['item_1'],'category_2':['item_2']} - item_capacities={'item_1':2,'item_2':5} - agent_category_capacities={'agent_1':{'category_1':5,'category_2':1},'agent_2':{'category_1':1,'category_2':0}} - item_valuations={'agent_1':{'item_1':0,'item_2':1},'agent_2':{'item_1':1,'item_2':0}} - items=['item_1','item_2'] - divide(algorithm=iterated_priority_matching, instance=Instance(valuations=item_valuations, item_capacities=item_capacities,items=items), - item_categories=item_categories, agent_category_capacities=agent_category_capacities, - ) + # item_categories={'category_1':['item_1'],'category_2':['item_2']} + # item_capacities={'item_1':2,'item_2':5} + # agent_category_capacities={'agent_1':{'category_1':5,'category_2':1},'agent_2':{'category_1':1,'category_2':0}} + # item_valuations={'agent_1':{'item_1':0,'item_2':1},'agent_2':{'item_1':1,'item_2':0}} + # items=['item_1','item_2'] + # divide(algorithm=iterated_priority_matching, instance=Instance(valuations=item_valuations, item_capacities=item_capacities,items=items), + # item_categories=item_categories, agent_category_capacities=agent_category_capacities, + #) From 842d3a8c36e9f8669f796e2b7aaab159f0908e5c Mon Sep 17 00:00:00 2001 From: Abodi Massarwa Date: Tue, 6 Aug 2024 06:59:27 +0300 Subject: [PATCH 17/36] minor modifications for the sake of testing --- .../algorithms/heterogeneous_matroid_constraints_algorithms.py | 1 + 1 file changed, 1 insertion(+) diff --git a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py index 2f37046..b98e037 100644 --- a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py +++ b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py @@ -87,6 +87,7 @@ def per_category_round_robin(alloc: AllocationBuilder, item_categories: dict[str logger.info(f'Envy graph before RR -> {envy_graph.nodes}, edges -> in {envy_graph.edges}') helper_categorization_friendly_picking_sequence(alloc, current_order, item_categories[category], agent_category_capacities, category) helper_update_envy_graph(alloc.bundles, valuation_func, envy_graph, item_categories, agent_category_capacities,callback) + callback(helper_generate_directed_graph_base64(envy_graph))#TODO remove logger.info(f'Envy graph after RR -> {envy_graph.nodes}, edges -> in {envy_graph.edges}') if not nx.is_directed_acyclic_graph(envy_graph): logger.info("Cycle removal started ") From 82fd585f1aa7df20a372df441087805bc5f1f739 Mon Sep 17 00:00:00 2001 From: Abodi Massarwa Date: Tue, 6 Aug 2024 07:01:09 +0300 Subject: [PATCH 18/36] minor modifications for the sake of testing --- .../heterogeneous_matroid_constraints_algorithms.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py index b98e037..e6969fb 100644 --- a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py +++ b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py @@ -87,7 +87,6 @@ def per_category_round_robin(alloc: AllocationBuilder, item_categories: dict[str logger.info(f'Envy graph before RR -> {envy_graph.nodes}, edges -> in {envy_graph.edges}') helper_categorization_friendly_picking_sequence(alloc, current_order, item_categories[category], agent_category_capacities, category) helper_update_envy_graph(alloc.bundles, valuation_func, envy_graph, item_categories, agent_category_capacities,callback) - callback(helper_generate_directed_graph_base64(envy_graph))#TODO remove logger.info(f'Envy graph after RR -> {envy_graph.nodes}, edges -> in {envy_graph.edges}') if not nx.is_directed_acyclic_graph(envy_graph): logger.info("Cycle removal started ") @@ -765,8 +764,8 @@ def helper_update_envy_graph(curr_bundles: dict, valuation_func: callable, envy_ #print(f"{agent1} envies {agent2}") # works great . # we need to add edge from the envier to the envyee envy_graph.add_edge(agent1, agent2) - if callback: - callback(helper_generate_directed_graph_base64(envy_graph)) + #if callback: + callback(helper_generate_directed_graph_base64(envy_graph)) logger.info(f"envy_graph.edges after update -> {envy_graph.edges}") # def visualize_graph(envy_graph): From 972f7c32fd2b88d8f149df3915400d16ec4e928b Mon Sep 17 00:00:00 2001 From: Abodi Massarwa Date: Tue, 6 Aug 2024 07:03:18 +0300 Subject: [PATCH 19/36] minor modifications for the sake of testing --- .../heterogeneous_matroid_constraints_algorithms.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py index e6969fb..8b3bf9a 100644 --- a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py +++ b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py @@ -739,6 +739,7 @@ def helper_update_envy_graph(curr_bundles: dict, valuation_func: callable, envy_ >>> graph.has_edge('Agent1','Agent2') False """ + callback(helper_generate_directed_graph_base64(envy_graph))#TODO remove #validate input if isinstance(curr_bundles, dict): for key, val in curr_bundles.items(): @@ -764,8 +765,8 @@ def helper_update_envy_graph(curr_bundles: dict, valuation_func: callable, envy_ #print(f"{agent1} envies {agent2}") # works great . # we need to add edge from the envier to the envyee envy_graph.add_edge(agent1, agent2) - #if callback: - callback(helper_generate_directed_graph_base64(envy_graph)) + if callback: + callback(helper_generate_directed_graph_base64(envy_graph)) logger.info(f"envy_graph.edges after update -> {envy_graph.edges}") # def visualize_graph(envy_graph): From 57f9075f19c90d243cae40c28e8fa5d86d11aebc Mon Sep 17 00:00:00 2001 From: Abodi Massarwa Date: Tue, 6 Aug 2024 07:04:54 +0300 Subject: [PATCH 20/36] =?UTF-8?q?final=20commit=20=F0=9F=99=8F=F0=9F=8F=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../algorithms/heterogeneous_matroid_constraints_algorithms.py | 1 - 1 file changed, 1 deletion(-) diff --git a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py index 8b3bf9a..2f37046 100644 --- a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py +++ b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py @@ -739,7 +739,6 @@ def helper_update_envy_graph(curr_bundles: dict, valuation_func: callable, envy_ >>> graph.has_edge('Agent1','Agent2') False """ - callback(helper_generate_directed_graph_base64(envy_graph))#TODO remove #validate input if isinstance(curr_bundles, dict): for key, val in curr_bundles.items(): From 44818f9fa5d340ce0a2119e0689cc21991f4789d Mon Sep 17 00:00:00 2001 From: Abodi Massarwa Date: Tue, 6 Aug 2024 07:19:47 +0300 Subject: [PATCH 21/36] =?UTF-8?q?hopefully=20final=20commit=20=F0=9F=99=8F?= =?UTF-8?q?=F0=9F=8F=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ogeneous_matroid_constraints_algorithms.py | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py index 2f37046..1c790a8 100644 --- a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py +++ b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py @@ -1218,19 +1218,19 @@ def helper_validate_item_categories(item_categories:dict[str, list]): def helper_generate_directed_graph_base64(graph, seed=42): - # plt.figure() - # plt.title('Envy Graph') - # pos = nx.spring_layout(graph, seed=seed) # Use a seed for reproducibility - # nx.draw(graph, pos, with_labels=True, node_color='lightblue', edge_color='gray', node_size=500, font_size=10, arrows=True) - # - # img_bytes = io.BytesIO() - # plt.savefig(img_bytes, format='png') - # plt.close() - # img_bytes.seek(0) - # - # base64_image = base64.b64encode(img_bytes.read()).decode('utf-8') - # print("Generated image data:", base64_image[:100]) # Print the first 100 characters of the image data - return 'base64_image' + plt.figure() + plt.title('Envy Graph') + pos = nx.spring_layout(graph, seed=seed) # Use a seed for reproducibility + nx.draw(graph, pos, with_labels=True, node_color='lightblue', edge_color='gray', node_size=500, font_size=10, arrows=True) + + img_bytes = io.BytesIO() + plt.savefig(img_bytes, format='png') + plt.close() + img_bytes.seek(0) + + base64_image = base64.b64encode(img_bytes.read()).decode('utf-8') + print("Generated image data:", base64_image[:100]) # Print the first 100 characters of the image data + return base64_image def helper_generate_bipartite_graph_base64(graph,iteration:int,category:str): From 2c11a206425a2609852850bc4b3412ee330b9e5d Mon Sep 17 00:00:00 2001 From: Abodi Massarwa Date: Thu, 8 Aug 2024 21:47:59 +0300 Subject: [PATCH 22/36] =?UTF-8?q?hopefully=20final=20commit=20:-)=20?= =?UTF-8?q?=F0=9F=99=8F=F0=9F=8F=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../heterogeneous_matroid_constraints_algorithms.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py index 1c790a8..6323e1a 100644 --- a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py +++ b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py @@ -21,6 +21,8 @@ import base64 logger = logging.getLogger(__name__) +# Create a string stream to capture logs +log_stream = io.StringIO() def per_category_round_robin(alloc: AllocationBuilder, item_categories: dict[str,list], agent_category_capacities: dict[str,dict[str,int]], initial_agent_order: list,callback:callable=None): @@ -1267,6 +1269,16 @@ def helper_generate_bipartite_graph_base64(graph,iteration:int,category:str): return base64.b64encode(img_bytes.read()).decode('utf-8') +def helper_configure_logger(): + stream_handler = logging.StreamHandler(log_stream) + formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') + logger.addHandler(stream_handler) + stream_handler.setFormatter(formatter) + logger.setLevel(logging.DEBUG) + +def helper_get_logs(): + return log_stream.getvalue() + if __name__ == "__main__": import doctest, sys #print("\n", doctest.testmod(), "\n") From 993a960ee65f61652b8e83a21d1d535563edaf6b Mon Sep 17 00:00:00 2001 From: Abodi Massarwa Date: Thu, 8 Aug 2024 22:08:20 +0300 Subject: [PATCH 23/36] =?UTF-8?q?final=20commit=F0=9F=99=8F=F0=9F=8F=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../algorithms/heterogeneous_matroid_constraints_algorithms.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py index 6323e1a..e667113 100644 --- a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py +++ b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py @@ -756,6 +756,8 @@ def helper_update_envy_graph(curr_bundles: dict, valuation_func: callable, envy_ logger.info(f"Creating envy graph for curr_bundles -> {curr_bundles}") envy_graph.clear_edges() envy_graph.add_nodes_from(curr_bundles.keys()) + if callback: + callback(helper_generate_directed_graph_base64(envy_graph)) for agent1, bundle1 in curr_bundles.items(): for agent2, bundle_agent2 in curr_bundles.items(): if agent1 is not agent2: # make sure w're not comparing same agent to himself From 1268cb78018ce2b13a8d8450afdb6e4f3806c5f0 Mon Sep 17 00:00:00 2001 From: Abodi Massarwa Date: Thu, 8 Aug 2024 22:52:54 +0300 Subject: [PATCH 24/36] =?UTF-8?q?final=20commit=F0=9F=99=8F=F0=9F=8F=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../heterogeneous_matroid_constraints_algorithms.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py index e667113..cc8d695 100644 --- a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py +++ b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py @@ -21,8 +21,7 @@ import base64 logger = logging.getLogger(__name__) -# Create a string stream to capture logs -log_stream = io.StringIO() + def per_category_round_robin(alloc: AllocationBuilder, item_categories: dict[str,list], agent_category_capacities: dict[str,dict[str,int]], initial_agent_order: list,callback:callable=None): @@ -1272,13 +1271,16 @@ def helper_generate_bipartite_graph_base64(graph,iteration:int,category:str): return base64.b64encode(img_bytes.read()).decode('utf-8') def helper_configure_logger(): + # Create a string stream to capture logs + log_stream = io.StringIO() stream_handler = logging.StreamHandler(log_stream) formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') logger.addHandler(stream_handler) stream_handler.setFormatter(formatter) logger.setLevel(logging.DEBUG) + return log_stream -def helper_get_logs(): +def helper_get_logs(log_stream): return log_stream.getvalue() if __name__ == "__main__": From 30f9bda1866ef313830a3dd3ea118697ccd0409e Mon Sep 17 00:00:00 2001 From: Abodi Massarwa Date: Sun, 11 Aug 2024 20:29:30 +0300 Subject: [PATCH 25/36] slight change in log formatter --- .../algorithms/heterogeneous_matroid_constraints_algorithms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py index cc8d695..b49e02c 100644 --- a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py +++ b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py @@ -1274,7 +1274,7 @@ def helper_configure_logger(): # Create a string stream to capture logs log_stream = io.StringIO() stream_handler = logging.StreamHandler(log_stream) - formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') + formatter = logging.Formatter('%(message)s') logger.addHandler(stream_handler) stream_handler.setFormatter(formatter) logger.setLevel(logging.DEBUG) From 07f14f2b8c75471a82c52fca4edaa2212955979f Mon Sep 17 00:00:00 2001 From: Abodi Massarwa Date: Sun, 11 Aug 2024 20:47:18 +0300 Subject: [PATCH 26/36] slight modifications in logger --- .../heterogeneous_matroid_constraints_algorithms.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py index b49e02c..9e8f06c 100644 --- a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py +++ b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py @@ -79,7 +79,7 @@ def per_category_round_robin(alloc: AllocationBuilder, item_categories: dict[str helper_validate_capacities(is_identical=True, agent_category_capacities=agent_category_capacities) helper_validate_valuations(agent_item_valuations=alloc.instance._valuations) # end validation - logger.info(f"Running per_category_round_robin with alloc -> {alloc.bundles} \n item_categories -> {item_categories} \n agent_category_capacities -> {agent_category_capacities} \n -> initial_agent_order are -> {initial_agent_order}\n ") + logger.info(f"**********\nRunning per_category_round_robin with alloc -> {alloc.bundles} \n item_categories -> {item_categories} \n agent_category_capacities -> {agent_category_capacities} \n -> initial_agent_order are -> {initial_agent_order}\n**********\n") envy_graph = nx.DiGraph() current_order = initial_agent_order valuation_func = alloc.instance.agent_item_value @@ -95,7 +95,7 @@ def per_category_round_robin(alloc: AllocationBuilder, item_categories: dict[str logger.info('cycle removal ended successfully ') current_order = list(nx.topological_sort(envy_graph)) logger.info(f"Topological sort -> {current_order} \n***************************** ") - logger.info(f'alloc after termination of algorithm ->{alloc}') + logger.info(f'alloc after per-category RR ->{alloc.bundles}') def capped_round_robin(alloc: AllocationBuilder,item_categories: dict[str,list], agent_category_capacities: dict[str,dict[str,int]], initial_agent_order: list, target_category: str): @@ -156,6 +156,8 @@ def capped_round_robin(alloc: AllocationBuilder,item_categories: dict[str,list], >>> divide(algorithm=capped_round_robin,instance=Instance(valuations=valuations,items=items),item_categories=item_categories,agent_category_capacities= agent_category_capacities,initial_agent_order = order,target_category=target_category) {'Agent1': [], 'Agent2': ['m1'], 'Agent3': ['m3', 'm4'], 'Agent4': ['m2', 'm5', 'm6']} """ + logger.info(f'**********\nRunning capped_round_robin\n**********') + #input validation helper_validate_item_categories(item_categories) helper_validate_duplicate(initial_agent_order) @@ -237,6 +239,8 @@ def two_categories_capped_round_robin(alloc: AllocationBuilder,item_categories: {'Agent1': ['m2', 'm3', 'm4'], 'Agent2': ['m5'], 'Agent3': ['m6']} >>> # m1 remains unallocated unfortunately :-( """ + logger.info(f'**********\nRunning two_categories_capped_round_robin\n**********') + #validate input helper_validate_item_categories(item_categories) helper_validate_duplicate(initial_agent_order) @@ -308,6 +312,8 @@ def per_category_capped_round_robin(alloc: AllocationBuilder,item_categories: di >>> divide(algorithm=per_category_capped_round_robin,instance=Instance(valuations=valuations,items=items),item_categories=item_categories,agent_category_capacities= agent_category_capacities,initial_agent_order = order) {'Agent1': ['m1'], 'Agent2': ['m2'], 'Agent3': ['m3']} """ + logger.info(f'**********\nRunning per-category-CRR\n**********') + #validate input helper_validate_item_categories(item_categories) helper_validate_duplicate(initial_agent_order) @@ -377,6 +383,7 @@ def iterated_priority_matching(alloc: AllocationBuilder, item_categories: dict[s >>> #divide(algorithm=iterated_priority_matching,instance=Instance(valuations=valuations,items=items),item_categories=item_categories,agent_category_capacities= agent_category_capacities)# m3 remains unallocated .... {'Agent1': ['m1', 'm5', 'm6'], 'Agent2': ['m2', 'm4'], 'Agent3': []} """ + logger.info(f'**********\nRunning Iterated priority matching\n**********') #validate input helper_validate_item_categories(item_categories) helper_validate_duplicate( @@ -621,6 +628,7 @@ def helper_categorization_friendly_picking_sequence(alloc:AllocationBuilder, age >>> alloc.sorted() {'agent1': ['m3', 'm7'], 'agent2': ['m1'], 'agent3': ['m2', 'm4', 'm5', 'm6']} """ + logger.info(f"**********\nRunning categorization_friendly_picking sequence with\nalloc->{alloc.bundles}, agent order -> {agent_order},items to allocate ->{items_to_allocate} , agent category capacities ->{agent_category_capacities} , target category ->{target_category}\n**********") #validate input helper_validate_duplicate(agent_order) helper_validate_duplicate(items_to_allocate) @@ -740,6 +748,7 @@ def helper_update_envy_graph(curr_bundles: dict, valuation_func: callable, envy_ >>> graph.has_edge('Agent1','Agent2') False """ + logger.info(f'**********\nRunning helper_update_envy_graph\n**********') #validate input if isinstance(curr_bundles, dict): for key, val in curr_bundles.items(): From d5f6f0f93e8d2ac85edee3554b46fda269f8250f Mon Sep 17 00:00:00 2001 From: Abodi Massarwa Date: Sun, 11 Aug 2024 20:53:11 +0300 Subject: [PATCH 27/36] slight modifications in logger --- ...rogeneous_matroid_constraints_algorithms.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py index 9e8f06c..3c8b4e5 100644 --- a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py +++ b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py @@ -541,6 +541,7 @@ def helper_envy(source: str, target: str, bundles: dict[str, set or list], val_f >>> helper_envy('agent3', 'agent2', alloc.bundles, val_func, item_categories, agent_category_capacities) False """ + logger.info(f'**********\nRunning helper_envy\n**********') #validate input if isinstance(bundles, dict): for key, val in bundles.items(): @@ -878,6 +879,8 @@ def helper_remove_cycles(envy_graph:nx.DiGraph, alloc:AllocationBuilder, valuati {'Agent1': ['Item3'], 'Agent2': ['Item1', 'Item2']} """ + logger.info(f'**********\nRunning helper_remove_cycles\n**********') + #start validation if not callable(valuation_func): raise ValueError("valuation_func must be callable.") @@ -959,6 +962,7 @@ def helper_update_ordered_agent_list(current_order: list, remaining_category_age >>> helper_update_ordered_agent_list(current_order, remaining_category_agent_capacities) ['Abed', 'Noor'] """ + logger.info(f'**********\nRunning helper_update_ordered_agent_list\n**********') #validate input helper_validate_duplicate(current_order) temp = {'catx': remaining_category_agent_capacities} @@ -1016,6 +1020,7 @@ def helper_update_item_list(alloc: AllocationBuilder, category: str, item_catego >>> helper_update_item_list(alloc, 'c2', item_categories) [] """ + logger.info(f'**********\nRunning helper_update_item_list\n**********') #validate input helper_validate_item_categories(item_categories) if not isinstance(category, str): @@ -1080,6 +1085,7 @@ def helper_priority_matching(agent_item_bipartite_graph:nx.Graph, current_order: >>> alloc.sorted() in [{'Agent1': ['Item3'], 'Agent2': ['Item2'], 'Agent3': ['Item1']} , {'Agent1': ['Item1'], 'Agent2': ['Item2'], 'Agent3': ['Item3']} , {'Agent1': ['Item1'], 'Agent2': ['Item3'], 'Agent3': ['Item2']}] True """ + logger.info(f'**********\nRunning helper_priority_matching\n**********') #validate input if not isinstance(agent_item_bipartite_graph ,nx.Graph): raise ValueError("agent_item_bipartite_graph must be of type nx.Graph.") @@ -1139,6 +1145,7 @@ def helper_create_agent_item_bipartite_graph(agents:list, items:list, valuation_ >>> sorted(bipartite_graph.edges(data=True)) [('Agent3', 'Item1', {'weight': 2}), ('Agent3', 'Item2', {'weight': 2}), ('Agent3', 'Item3', {'weight': 2})] """ + logger.info(f'**********\nRunning helper_create_agent_item_bipartite_graph\n**********') #validate input helper_validate_duplicate(agents) helper_validate_duplicate(items) @@ -1162,6 +1169,8 @@ def helper_create_agent_item_bipartite_graph(agents:list, items:list, valuation_ return agent_item_bipartite_graph def helper_validate_valuations(agent_item_valuations: dict[str, dict[str, int]], is_identical: bool = False, is_binary: bool = False): + logger.info(f'**********\nRunning helper_validate_valuations\n**********') + if isinstance(agent_item_valuations,dict):# to check that the agent_category_capacities is indeed dict[str,dict[str,int]] for key,value in agent_item_valuations.items(): if not isinstance(key,str) or not isinstance(value,dict): @@ -1191,6 +1200,8 @@ def helper_validate_valuations(agent_item_valuations: dict[str, dict[str, int]], def helper_validate_capacities(agent_category_capacities: dict[str, dict[str, int]], is_identical: bool = False): + logger.info(f'**********\nRunning helper_validate_capacities\n**********') + if isinstance(agent_category_capacities,dict):# to check that the agent_category_capacities is indeed dict[str,dict[str,int]] for key,value in agent_category_capacities.items(): if not isinstance(key,str) or not isinstance(value,dict): @@ -1214,6 +1225,7 @@ def helper_validate_capacities(agent_category_capacities: dict[str, dict[str, in raise ValueError(f"agent_category_capacities {agent_category_capacities} isn't structured correctly") def helper_validate_duplicate(list_of_items:list): + logger.info(f'**********\nRunning helper_validate_duplicates\n**********') if isinstance(list_of_items,list): if len(list_of_items) != len(set(list_of_items)): raise ValueError(f"Duplicate items found in the list: {list_of_items}.") @@ -1221,6 +1233,7 @@ def helper_validate_duplicate(list_of_items:list): raise ValueError(f"the input {list_of_items} isn't of type list, only list is allowed.") def helper_validate_item_categories(item_categories:dict[str, list]): + logger.info(f'**********\nRunning helper_validate_item_categories\n**********') if isinstance(item_categories, dict): for category, items in item_categories.items(): if not isinstance(category, str) or not isinstance(items, list): @@ -1230,6 +1243,8 @@ def helper_validate_item_categories(item_categories:dict[str, list]): def helper_generate_directed_graph_base64(graph, seed=42): + logger.info(f'**********\nRunning helper_generate_directed_graph_base64\n**********') + plt.figure() plt.title('Envy Graph') pos = nx.spring_layout(graph, seed=seed) # Use a seed for reproducibility @@ -1246,6 +1261,7 @@ def helper_generate_directed_graph_base64(graph, seed=42): def helper_generate_bipartite_graph_base64(graph,iteration:int,category:str): + logger.info(f'**********\nRunning helper_generate_bipartite_graph_base64\n**********') plt.figure() plt.title('Agent-Item Bipartite Graph', fontsize=16) additional_text=f'{category} iteration {iteration}' @@ -1280,6 +1296,7 @@ def helper_generate_bipartite_graph_base64(graph,iteration:int,category:str): return base64.b64encode(img_bytes.read()).decode('utf-8') def helper_configure_logger(): + logger.info(f'**********\nRunning helper_configure_logger\n**********') # Create a string stream to capture logs log_stream = io.StringIO() stream_handler = logging.StreamHandler(log_stream) @@ -1290,6 +1307,7 @@ def helper_configure_logger(): return log_stream def helper_get_logs(log_stream): + logger.info(f'**********\nRunning helper_get_logs\n**********') return log_stream.getvalue() if __name__ == "__main__": From ac41343f1793e2859b2c6c02f202788287cae359 Mon Sep 17 00:00:00 2001 From: Abodi Massarwa Date: Wed, 14 Aug 2024 05:46:32 +0300 Subject: [PATCH 28/36] removed extra headache log messages ! --- ...ogeneous_matroid_constraints_algorithms.py | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py index 3c8b4e5..8f67ebf 100644 --- a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py +++ b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py @@ -79,7 +79,7 @@ def per_category_round_robin(alloc: AllocationBuilder, item_categories: dict[str helper_validate_capacities(is_identical=True, agent_category_capacities=agent_category_capacities) helper_validate_valuations(agent_item_valuations=alloc.instance._valuations) # end validation - logger.info(f"**********\nRunning per_category_round_robin with alloc -> {alloc.bundles} \n item_categories -> {item_categories} \n agent_category_capacities -> {agent_category_capacities} \n -> initial_agent_order are -> {initial_agent_order}\n**********\n") + #logger.info(f"**********\nRunning per_category_round_robin with alloc -> {alloc.bundles} \n item_categories -> {item_categories} \n agent_category_capacities -> {agent_category_capacities} \n -> initial_agent_order are -> {initial_agent_order}\n**********\n") envy_graph = nx.DiGraph() current_order = initial_agent_order valuation_func = alloc.instance.agent_item_value @@ -156,7 +156,7 @@ def capped_round_robin(alloc: AllocationBuilder,item_categories: dict[str,list], >>> divide(algorithm=capped_round_robin,instance=Instance(valuations=valuations,items=items),item_categories=item_categories,agent_category_capacities= agent_category_capacities,initial_agent_order = order,target_category=target_category) {'Agent1': [], 'Agent2': ['m1'], 'Agent3': ['m3', 'm4'], 'Agent4': ['m2', 'm5', 'm6']} """ - logger.info(f'**********\nRunning capped_round_robin\n**********') + #logger.info(f'**********\nRunning capped_round_robin\n**********') #input validation helper_validate_item_categories(item_categories) @@ -170,7 +170,7 @@ def capped_round_robin(alloc: AllocationBuilder,item_categories: dict[str,list], # end validation # no need for envy graphs whatsoever current_order = initial_agent_order - logger.info(f'Running Capped Round Robin. initial_agent_order -> {initial_agent_order}') + logger.info(f'**********\nRunning Capped Round Robin. initial_agent_order -> {initial_agent_order}') helper_categorization_friendly_picking_sequence(alloc, current_order, item_categories[target_category], agent_category_capacities, target_category=target_category) # this is RR without wrapper logger.info(f'alloc after CRR -> {alloc.bundles}') @@ -239,7 +239,7 @@ def two_categories_capped_round_robin(alloc: AllocationBuilder,item_categories: {'Agent1': ['m2', 'm3', 'm4'], 'Agent2': ['m5'], 'Agent3': ['m6']} >>> # m1 remains unallocated unfortunately :-( """ - logger.info(f'**********\nRunning two_categories_capped_round_robin\n**********') + #logger.info(f'**********\nRunning two_categories_capped_round_robin') #validate input helper_validate_item_categories(item_categories) @@ -253,7 +253,7 @@ def two_categories_capped_round_robin(alloc: AllocationBuilder,item_categories: f"Not all elements of the tuple {target_category_pair} are in the categories list {list(item_categories.keys())}.") #end validation current_order = initial_agent_order - logger.info(f'\nRunning two_categories_capped_round_robin, initial_agent_order -> {current_order}') + logger.info(f'**********\nRunning two_categories_capped_round_robin, initial_agent_order -> {current_order}') logger.info(f'\nAllocating cagetory {target_category_pair[0]}') helper_categorization_friendly_picking_sequence(alloc, current_order, item_categories[target_category_pair[0]], agent_category_capacities, target_category=target_category_pair[0]) #calling CRR on first category @@ -312,7 +312,7 @@ def per_category_capped_round_robin(alloc: AllocationBuilder,item_categories: di >>> divide(algorithm=per_category_capped_round_robin,instance=Instance(valuations=valuations,items=items),item_categories=item_categories,agent_category_capacities= agent_category_capacities,initial_agent_order = order) {'Agent1': ['m1'], 'Agent2': ['m2'], 'Agent3': ['m3']} """ - logger.info(f'**********\nRunning per-category-CRR\n**********') + #logger.info(f'**********\nRunning per-category-CRR\n**********') #validate input helper_validate_item_categories(item_categories) @@ -326,7 +326,7 @@ def per_category_capped_round_robin(alloc: AllocationBuilder,item_categories: di envy_graph = nx.DiGraph() current_order = initial_agent_order valuation_func = alloc.instance.agent_item_value - logger.info(f'Run Per-Category Capped Round Robin, initial_agent_order->{initial_agent_order}') + logger.info(f'**********\nRunning Per-Category Capped Round Robin, initial_agent_order->{initial_agent_order}') for category in item_categories.keys(): helper_categorization_friendly_picking_sequence(alloc=alloc, agent_order=current_order, items_to_allocate=item_categories[category], @@ -383,7 +383,7 @@ def iterated_priority_matching(alloc: AllocationBuilder, item_categories: dict[s >>> #divide(algorithm=iterated_priority_matching,instance=Instance(valuations=valuations,items=items),item_categories=item_categories,agent_category_capacities= agent_category_capacities)# m3 remains unallocated .... {'Agent1': ['m1', 'm5', 'm6'], 'Agent2': ['m2', 'm4'], 'Agent3': []} """ - logger.info(f'**********\nRunning Iterated priority matching\n**********') + logger.info(f'**********\nRunning Iterated priority matching') #validate input helper_validate_item_categories(item_categories) helper_validate_duplicate( @@ -392,7 +392,7 @@ def iterated_priority_matching(alloc: AllocationBuilder, item_categories: dict[s helper_validate_valuations(agent_item_valuations=alloc.instance._valuations, is_binary=True) #end validation - logger.info("Running Iterated Priority Matching") + #logger.info("Running Iterated Priority Matching") envy_graph = nx.DiGraph() envy_graph.add_nodes_from(alloc.remaining_agents()) # adding agent nodes (no edges involved yet) current_order = list(alloc.remaining_agents()) # in this algorithm no need for initial_agent_order @@ -541,7 +541,7 @@ def helper_envy(source: str, target: str, bundles: dict[str, set or list], val_f >>> helper_envy('agent3', 'agent2', alloc.bundles, val_func, item_categories, agent_category_capacities) False """ - logger.info(f'**********\nRunning helper_envy\n**********') + #logger.info(f'**********\nRunning helper_envy\n**********') #validate input if isinstance(bundles, dict): for key, val in bundles.items(): @@ -629,7 +629,7 @@ def helper_categorization_friendly_picking_sequence(alloc:AllocationBuilder, age >>> alloc.sorted() {'agent1': ['m3', 'm7'], 'agent2': ['m1'], 'agent3': ['m2', 'm4', 'm5', 'm6']} """ - logger.info(f"**********\nRunning categorization_friendly_picking sequence with\nalloc->{alloc.bundles}, agent order -> {agent_order},items to allocate ->{items_to_allocate} , agent category capacities ->{agent_category_capacities} , target category ->{target_category}\n**********") + #logger.info(f"**********\nRunning categorization_friendly_picking sequence with\nalloc->{alloc.bundles}, agent order -> {agent_order},items to allocate ->{items_to_allocate} , agent category capacities ->{agent_category_capacities} , target category ->{target_category}\n**********") #validate input helper_validate_duplicate(agent_order) helper_validate_duplicate(items_to_allocate) @@ -749,7 +749,7 @@ def helper_update_envy_graph(curr_bundles: dict, valuation_func: callable, envy_ >>> graph.has_edge('Agent1','Agent2') False """ - logger.info(f'**********\nRunning helper_update_envy_graph\n**********') + #logger.info(f'**********\nRunning helper_update_envy_graph\n**********') #validate input if isinstance(curr_bundles, dict): for key, val in curr_bundles.items(): @@ -879,7 +879,7 @@ def helper_remove_cycles(envy_graph:nx.DiGraph, alloc:AllocationBuilder, valuati {'Agent1': ['Item3'], 'Agent2': ['Item1', 'Item2']} """ - logger.info(f'**********\nRunning helper_remove_cycles\n**********') + #logger.info(f'**********\nRunning helper_remove_cycles\n**********') #start validation if not callable(valuation_func): @@ -962,7 +962,7 @@ def helper_update_ordered_agent_list(current_order: list, remaining_category_age >>> helper_update_ordered_agent_list(current_order, remaining_category_agent_capacities) ['Abed', 'Noor'] """ - logger.info(f'**********\nRunning helper_update_ordered_agent_list\n**********') + #logger.info(f'**********\nRunning helper_update_ordered_agent_list\n**********') #validate input helper_validate_duplicate(current_order) temp = {'catx': remaining_category_agent_capacities} @@ -1020,7 +1020,7 @@ def helper_update_item_list(alloc: AllocationBuilder, category: str, item_catego >>> helper_update_item_list(alloc, 'c2', item_categories) [] """ - logger.info(f'**********\nRunning helper_update_item_list\n**********') + #logger.info(f'**********\nRunning helper_update_item_list\n**********') #validate input helper_validate_item_categories(item_categories) if not isinstance(category, str): @@ -1085,7 +1085,7 @@ def helper_priority_matching(agent_item_bipartite_graph:nx.Graph, current_order: >>> alloc.sorted() in [{'Agent1': ['Item3'], 'Agent2': ['Item2'], 'Agent3': ['Item1']} , {'Agent1': ['Item1'], 'Agent2': ['Item2'], 'Agent3': ['Item3']} , {'Agent1': ['Item1'], 'Agent2': ['Item3'], 'Agent3': ['Item2']}] True """ - logger.info(f'**********\nRunning helper_priority_matching\n**********') + #logger.info(f'**********\nRunning helper_priority_matching\n**********') #validate input if not isinstance(agent_item_bipartite_graph ,nx.Graph): raise ValueError("agent_item_bipartite_graph must be of type nx.Graph.") @@ -1145,7 +1145,7 @@ def helper_create_agent_item_bipartite_graph(agents:list, items:list, valuation_ >>> sorted(bipartite_graph.edges(data=True)) [('Agent3', 'Item1', {'weight': 2}), ('Agent3', 'Item2', {'weight': 2}), ('Agent3', 'Item3', {'weight': 2})] """ - logger.info(f'**********\nRunning helper_create_agent_item_bipartite_graph\n**********') + #logger.info(f'**********\nRunning helper_create_agent_item_bipartite_graph\n**********') #validate input helper_validate_duplicate(agents) helper_validate_duplicate(items) @@ -1169,7 +1169,7 @@ def helper_create_agent_item_bipartite_graph(agents:list, items:list, valuation_ return agent_item_bipartite_graph def helper_validate_valuations(agent_item_valuations: dict[str, dict[str, int]], is_identical: bool = False, is_binary: bool = False): - logger.info(f'**********\nRunning helper_validate_valuations\n**********') + #logger.info(f'**********\nRunning helper_validate_valuations\n**********') if isinstance(agent_item_valuations,dict):# to check that the agent_category_capacities is indeed dict[str,dict[str,int]] for key,value in agent_item_valuations.items(): @@ -1200,7 +1200,7 @@ def helper_validate_valuations(agent_item_valuations: dict[str, dict[str, int]], def helper_validate_capacities(agent_category_capacities: dict[str, dict[str, int]], is_identical: bool = False): - logger.info(f'**********\nRunning helper_validate_capacities\n**********') + #logger.info(f'**********\nRunning helper_validate_capacities\n**********') if isinstance(agent_category_capacities,dict):# to check that the agent_category_capacities is indeed dict[str,dict[str,int]] for key,value in agent_category_capacities.items(): @@ -1225,7 +1225,7 @@ def helper_validate_capacities(agent_category_capacities: dict[str, dict[str, in raise ValueError(f"agent_category_capacities {agent_category_capacities} isn't structured correctly") def helper_validate_duplicate(list_of_items:list): - logger.info(f'**********\nRunning helper_validate_duplicates\n**********') + #logger.info(f'**********\nRunning helper_validate_duplicates\n**********') if isinstance(list_of_items,list): if len(list_of_items) != len(set(list_of_items)): raise ValueError(f"Duplicate items found in the list: {list_of_items}.") @@ -1233,7 +1233,7 @@ def helper_validate_duplicate(list_of_items:list): raise ValueError(f"the input {list_of_items} isn't of type list, only list is allowed.") def helper_validate_item_categories(item_categories:dict[str, list]): - logger.info(f'**********\nRunning helper_validate_item_categories\n**********') + #logger.info(f'**********\nRunning helper_validate_item_categories\n**********') if isinstance(item_categories, dict): for category, items in item_categories.items(): if not isinstance(category, str) or not isinstance(items, list): @@ -1243,7 +1243,7 @@ def helper_validate_item_categories(item_categories:dict[str, list]): def helper_generate_directed_graph_base64(graph, seed=42): - logger.info(f'**********\nRunning helper_generate_directed_graph_base64\n**********') + #logger.info(f'**********\nRunning helper_generate_directed_graph_base64\n**********') plt.figure() plt.title('Envy Graph') @@ -1261,7 +1261,7 @@ def helper_generate_directed_graph_base64(graph, seed=42): def helper_generate_bipartite_graph_base64(graph,iteration:int,category:str): - logger.info(f'**********\nRunning helper_generate_bipartite_graph_base64\n**********') + #logger.info(f'**********\nRunning helper_generate_bipartite_graph_base64\n**********') plt.figure() plt.title('Agent-Item Bipartite Graph', fontsize=16) additional_text=f'{category} iteration {iteration}' @@ -1296,7 +1296,7 @@ def helper_generate_bipartite_graph_base64(graph,iteration:int,category:str): return base64.b64encode(img_bytes.read()).decode('utf-8') def helper_configure_logger(): - logger.info(f'**********\nRunning helper_configure_logger\n**********') + #logger.info(f'**********\nRunning helper_configure_logger\n**********') # Create a string stream to capture logs log_stream = io.StringIO() stream_handler = logging.StreamHandler(log_stream) @@ -1307,7 +1307,7 @@ def helper_configure_logger(): return log_stream def helper_get_logs(log_stream): - logger.info(f'**********\nRunning helper_get_logs\n**********') + #logger.info(f'**********\nRunning helper_get_logs\n**********') return log_stream.getvalue() if __name__ == "__main__": From 643bb9dd4f76f8ef1c45ef556a7fdb6dee10340b Mon Sep 17 00:00:00 2001 From: Abodi Massarwa Date: Fri, 16 Aug 2024 20:17:53 +0300 Subject: [PATCH 29/36] minor changes --- ...ogeneous_matroid_constraints_algorithms.py | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py index 8f67ebf..bdc0fc5 100644 --- a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py +++ b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py @@ -83,15 +83,17 @@ def per_category_round_robin(alloc: AllocationBuilder, item_categories: dict[str envy_graph = nx.DiGraph() current_order = initial_agent_order valuation_func = alloc.instance.agent_item_value + iter=0 for category in item_categories.keys(): + iter+=1 logger.info(f'\nCurrent category -> {category}') logger.info(f'Envy graph before RR -> {envy_graph.nodes}, edges -> in {envy_graph.edges}') helper_categorization_friendly_picking_sequence(alloc, current_order, item_categories[category], agent_category_capacities, category) - helper_update_envy_graph(alloc.bundles, valuation_func, envy_graph, item_categories, agent_category_capacities,callback) + helper_update_envy_graph(alloc.bundles, valuation_func, envy_graph, item_categories, agent_category_capacities,callback,category=category,iteration=iter) logger.info(f'Envy graph after RR -> {envy_graph.nodes}, edges -> in {envy_graph.edges}') if not nx.is_directed_acyclic_graph(envy_graph): logger.info("Cycle removal started ") - helper_remove_cycles(envy_graph, alloc, valuation_func, item_categories, agent_category_capacities,callback) + helper_remove_cycles(envy_graph, alloc, valuation_func, item_categories, agent_category_capacities,callback,category=category,iteration=iter) logger.info('cycle removal ended successfully ') current_order = list(nx.topological_sort(envy_graph)) logger.info(f"Topological sort -> {current_order} \n***************************** ") @@ -327,13 +329,15 @@ def per_category_capped_round_robin(alloc: AllocationBuilder,item_categories: di current_order = initial_agent_order valuation_func = alloc.instance.agent_item_value logger.info(f'**********\nRunning Per-Category Capped Round Robin, initial_agent_order->{initial_agent_order}') + iter=0 for category in item_categories.keys(): + iter+=1 helper_categorization_friendly_picking_sequence(alloc=alloc, agent_order=current_order, items_to_allocate=item_categories[category], agent_category_capacities=agent_category_capacities, target_category=category) helper_update_envy_graph(curr_bundles=alloc.bundles, valuation_func=valuation_func, envy_graph=envy_graph, - item_categories=item_categories, agent_category_capacities=agent_category_capacities) + item_categories=item_categories, agent_category_capacities=agent_category_capacities,category=category,iteration=iter) current_order = list(nx.topological_sort(envy_graph)) logger.info(f'alloc after RR in category ->{category} is ->{alloc.bundles}.\n Envy graph nodes->{envy_graph.nodes} edges->{envy_graph.edges}.\ntopological sort->{current_order}') logger.info(f'allocation after termination of algorithm4 -> {alloc.bundles}') @@ -428,7 +432,7 @@ def iterated_priority_matching(alloc: AllocationBuilder, item_categories: dict[s # Creation of envy graph helper_update_envy_graph(curr_bundles=alloc.bundles, valuation_func=valuation_func, envy_graph=envy_graph, item_categories=item_categories, - agent_category_capacities=agent_category_capacities,callback=callback) # updating envy graph with respect to matchings (first iteration we get no envy, cause there is no matching) + agent_category_capacities=agent_category_capacities,callback=callback,category=category,iteration=i) # updating envy graph with respect to matchings (first iteration we get no envy, cause there is no matching) #topological sort (papers prove graph is always a-cyclic) topological_sort = list(nx.topological_sort(envy_graph)) logger.info(f'topological sort is -> {topological_sort}') @@ -698,7 +702,7 @@ def helper_categorization_friendly_picking_sequence(alloc:AllocationBuilder, age def helper_update_envy_graph(curr_bundles: dict, valuation_func: callable, envy_graph: DiGraph, item_categories: dict[str,list], - agent_category_capacities: dict[str,dict[str,int]],callback:callable=None): + agent_category_capacities: dict[str,dict[str,int]],callback:callable=None,category:str='',iteration:int=0): """ simply a helper function to update the envy-graph based on given params :param curr_bundles: the current allocation @@ -766,7 +770,7 @@ def helper_update_envy_graph(curr_bundles: dict, valuation_func: callable, envy_ envy_graph.clear_edges() envy_graph.add_nodes_from(curr_bundles.keys()) if callback: - callback(helper_generate_directed_graph_base64(envy_graph)) + callback(helper_generate_directed_graph_base64(envy_graph,category=category,iteration=iteration)) for agent1, bundle1 in curr_bundles.items(): for agent2, bundle_agent2 in curr_bundles.items(): if agent1 is not agent2: # make sure w're not comparing same agent to himself @@ -778,7 +782,7 @@ def helper_update_envy_graph(curr_bundles: dict, valuation_func: callable, envy_ # we need to add edge from the envier to the envyee envy_graph.add_edge(agent1, agent2) if callback: - callback(helper_generate_directed_graph_base64(envy_graph)) + callback(helper_generate_directed_graph_base64(envy_graph,category=category,iteration=iteration)) logger.info(f"envy_graph.edges after update -> {envy_graph.edges}") # def visualize_graph(envy_graph): @@ -789,7 +793,7 @@ def helper_update_envy_graph(curr_bundles: dict, valuation_func: callable, envy_ # plt.show() # -def helper_remove_cycles(envy_graph:nx.DiGraph, alloc:AllocationBuilder, valuation_func:callable, item_categories:dict[str,list], agent_category_capacities:dict[str,dict[str,int]],callback:callable=None): +def helper_remove_cycles(envy_graph:nx.DiGraph, alloc:AllocationBuilder, valuation_func:callable, item_categories:dict[str,list], agent_category_capacities:dict[str,dict[str,int]],callback:callable=None,iteration:int=-1,category:str=''): """ Removes cycles from the envy graph by updating the bundles. @@ -914,10 +918,10 @@ def helper_remove_cycles(envy_graph:nx.DiGraph, alloc:AllocationBuilder, valuati logger.info(f"Updated temp_val: {temp_val}") # Update the envy graph after swapping - helper_update_envy_graph(alloc.bundles, valuation_func, envy_graph, item_categories, agent_category_capacities) + helper_update_envy_graph(alloc.bundles, valuation_func, envy_graph, item_categories, agent_category_capacities,category=category,iteration=iteration) #callback section for our flask_app - if callback: - callback(helper_generate_directed_graph_base64(envy_graph)) + # if callback: + # callback(helper_generate_directed_graph_base64(envy_graph)) logger.info(f"Updated envy graph. is Graph acyclic ?? {nx.is_directed_acyclic_graph(envy_graph)}") @@ -1242,11 +1246,13 @@ def helper_validate_item_categories(item_categories:dict[str, list]): raise ValueError(f"item categories is supposed to be dict[str,list] but u entered {type(item_categories)}") -def helper_generate_directed_graph_base64(graph, seed=42): +def helper_generate_directed_graph_base64(graph, seed=42,category:str='',iteration:int=None): #logger.info(f'**********\nRunning helper_generate_directed_graph_base64\n**********') plt.figure() plt.title('Envy Graph') + additional_text=f'{category} iteration {iteration}' + plt.figtext(0.5, 0.95, additional_text, wrap=True, horizontalalignment='center', fontsize=10) pos = nx.spring_layout(graph, seed=seed) # Use a seed for reproducibility nx.draw(graph, pos, with_labels=True, node_color='lightblue', edge_color='gray', node_size=500, font_size=10, arrows=True) @@ -1324,6 +1330,7 @@ def helper_get_logs(log_stream): valuations = {'Agent1':{'m1':2,'m2':1,'m3':1,'m4':10},'Agent2':{'m1':1,'m2':2,'m3':1,'m4':10},'Agent3':{'m1':1,'m2':1,'m3':2,'m4':10},'Agent4':{'m1':1,'m2':1,'m3':1,'m4':10}} sum_agent_category_capacities={agent:sum(cap.values()) for agent,cap in agent_category_capacities.items()} instance=Instance(valuations=valuations,items=items,agent_capacities=sum_agent_category_capacities) + print(instance) divide(algorithm=per_category_round_robin,instance=instance,item_categories=item_categories,agent_category_capacities=agent_category_capacities,initial_agent_order=order) # divide(algorithm=two_categories_capped_round_robin,instance=instance,item_categories=item_categories,agent_category_capacities=agent_category_capacities,initial_agent_order=order,target_category_pair=("c1","c2")) # From 6c7b5494c0d1ffc33ade38e38774ede3ab3e909d Mon Sep 17 00:00:00 2001 From: Abodi Massarwa Date: Fri, 16 Aug 2024 20:27:11 +0300 Subject: [PATCH 30/36] minor changes --- .../heterogeneous_matroid_constraints_algorithms.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py index bdc0fc5..cbac2bd 100644 --- a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py +++ b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py @@ -1250,9 +1250,9 @@ def helper_generate_directed_graph_base64(graph, seed=42,category:str='',iterati #logger.info(f'**********\nRunning helper_generate_directed_graph_base64\n**********') plt.figure() - plt.title('Envy Graph') - additional_text=f'{category} iteration {iteration}' - plt.figtext(0.5, 0.95, additional_text, wrap=True, horizontalalignment='center', fontsize=10) + plt.title('Envy Graph',fontsize=16) + additional_text=f'category -> {category} iteration -> {iteration}' + plt.figtext(0.5, 0.90, additional_text, wrap=True, horizontalalignment='center', fontsize=10) pos = nx.spring_layout(graph, seed=seed) # Use a seed for reproducibility nx.draw(graph, pos, with_labels=True, node_color='lightblue', edge_color='gray', node_size=500, font_size=10, arrows=True) @@ -1270,8 +1270,8 @@ def helper_generate_bipartite_graph_base64(graph,iteration:int,category:str): #logger.info(f'**********\nRunning helper_generate_bipartite_graph_base64\n**********') plt.figure() plt.title('Agent-Item Bipartite Graph', fontsize=16) - additional_text=f'{category} iteration {iteration}' - plt.figtext(0.5, 0.95, additional_text, wrap=True, horizontalalignment='center', fontsize=10) + additional_text=f'category -> {category} iteration -> {iteration}' + plt.figtext(0.5, 0.90, additional_text, wrap=True, horizontalalignment='center', fontsize=10) try: top_nodes = {n for n, d in graph.nodes(data=True) if d['bipartite'] == 0} bottom_nodes = set(graph) - top_nodes From a54a91904094b938e1b54a5fd3cb274861487fa0 Mon Sep 17 00:00:00 2001 From: Abodi Massarwa Date: Fri, 16 Aug 2024 20:38:48 +0300 Subject: [PATCH 31/36] minor changes --- .../heterogeneous_matroid_constraints_algorithms.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py index cbac2bd..dbea82e 100644 --- a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py +++ b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py @@ -269,7 +269,7 @@ def two_categories_capped_round_robin(alloc: AllocationBuilder,item_categories: def per_category_capped_round_robin(alloc: AllocationBuilder,item_categories: dict[str,list], agent_category_capacities: dict[str,dict[str,int]], - initial_agent_order: list): + initial_agent_order: list,callback:callable=None): """ this is Algorithm 4 deals with (Different Capacities, Identical Valuations), suitable for any number of categories CRR (per-category capped round-robin) algorithm @@ -337,7 +337,7 @@ def per_category_capped_round_robin(alloc: AllocationBuilder,item_categories: di agent_category_capacities=agent_category_capacities, target_category=category) helper_update_envy_graph(curr_bundles=alloc.bundles, valuation_func=valuation_func, envy_graph=envy_graph, - item_categories=item_categories, agent_category_capacities=agent_category_capacities,category=category,iteration=iter) + item_categories=item_categories, agent_category_capacities=agent_category_capacities,category=category,iteration=iter,callback=callback) current_order = list(nx.topological_sort(envy_graph)) logger.info(f'alloc after RR in category ->{category} is ->{alloc.bundles}.\n Envy graph nodes->{envy_graph.nodes} edges->{envy_graph.edges}.\ntopological sort->{current_order}') logger.info(f'allocation after termination of algorithm4 -> {alloc.bundles}') From 1b7d98d941911334ad1824c9a586055c2319541e Mon Sep 17 00:00:00 2001 From: Abodi Massarwa Date: Fri, 16 Aug 2024 20:44:29 +0300 Subject: [PATCH 32/36] minor changes --- .../heterogeneous_matroid_constraints_algorithms.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py index dbea82e..d773136 100644 --- a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py +++ b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py @@ -1252,7 +1252,7 @@ def helper_generate_directed_graph_base64(graph, seed=42,category:str='',iterati plt.figure() plt.title('Envy Graph',fontsize=16) additional_text=f'category -> {category} iteration -> {iteration}' - plt.figtext(0.5, 0.90, additional_text, wrap=True, horizontalalignment='center', fontsize=10) + plt.figtext(0.5, 0.85, additional_text, wrap=True, horizontalalignment='center', fontsize=10) pos = nx.spring_layout(graph, seed=seed) # Use a seed for reproducibility nx.draw(graph, pos, with_labels=True, node_color='lightblue', edge_color='gray', node_size=500, font_size=10, arrows=True) @@ -1271,7 +1271,7 @@ def helper_generate_bipartite_graph_base64(graph,iteration:int,category:str): plt.figure() plt.title('Agent-Item Bipartite Graph', fontsize=16) additional_text=f'category -> {category} iteration -> {iteration}' - plt.figtext(0.5, 0.90, additional_text, wrap=True, horizontalalignment='center', fontsize=10) + plt.figtext(0.5, 0.85, additional_text, wrap=True, horizontalalignment='center', fontsize=10) try: top_nodes = {n for n, d in graph.nodes(data=True) if d['bipartite'] == 0} bottom_nodes = set(graph) - top_nodes From e8757e3dfcc959511a8a7ef1fd78a4e6f80b7f52 Mon Sep 17 00:00:00 2001 From: Abodi Massarwa Date: Sat, 17 Aug 2024 23:59:02 +0300 Subject: [PATCH 33/36] improved image capturing performance by removing it from loops --- ...ogeneous_matroid_constraints_algorithms.py | 45 ++++++++++++------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py index d773136..4816c44 100644 --- a/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py +++ b/fairpyx/algorithms/heterogeneous_matroid_constraints_algorithms.py @@ -769,8 +769,6 @@ def helper_update_envy_graph(curr_bundles: dict, valuation_func: callable, envy_ logger.info(f"Creating envy graph for curr_bundles -> {curr_bundles}") envy_graph.clear_edges() envy_graph.add_nodes_from(curr_bundles.keys()) - if callback: - callback(helper_generate_directed_graph_base64(envy_graph,category=category,iteration=iteration)) for agent1, bundle1 in curr_bundles.items(): for agent2, bundle_agent2 in curr_bundles.items(): if agent1 is not agent2: # make sure w're not comparing same agent to himself @@ -781,8 +779,8 @@ def helper_update_envy_graph(curr_bundles: dict, valuation_func: callable, envy_ #print(f"{agent1} envies {agent2}") # works great . # we need to add edge from the envier to the envyee envy_graph.add_edge(agent1, agent2) - if callback: - callback(helper_generate_directed_graph_base64(envy_graph,category=category,iteration=iteration)) + if callback:# no need to capture image in each iteration ... + callback(helper_generate_directed_graph_base64(envy_graph, category=category, iteration=iteration)) logger.info(f"envy_graph.edges after update -> {envy_graph.edges}") # def visualize_graph(envy_graph): @@ -1246,12 +1244,12 @@ def helper_validate_item_categories(item_categories:dict[str, list]): raise ValueError(f"item categories is supposed to be dict[str,list] but u entered {type(item_categories)}") -def helper_generate_directed_graph_base64(graph, seed=42,category:str='',iteration:int=None): +def helper_generate_directed_graph_base64(graph, seed=42,category:str='',iteration:int=None,text:str=None): #logger.info(f'**********\nRunning helper_generate_directed_graph_base64\n**********') plt.figure() plt.title('Envy Graph',fontsize=16) - additional_text=f'category -> {category} iteration -> {iteration}' + additional_text=f'category -> {category} iteration -> {iteration}' if text is None else text plt.figtext(0.5, 0.85, additional_text, wrap=True, horizontalalignment='center', fontsize=10) pos = nx.spring_layout(graph, seed=seed) # Use a seed for reproducibility nx.draw(graph, pos, with_labels=True, node_color='lightblue', edge_color='gray', node_size=500, font_size=10, arrows=True) @@ -1266,11 +1264,11 @@ def helper_generate_directed_graph_base64(graph, seed=42,category:str='',iterati return base64_image -def helper_generate_bipartite_graph_base64(graph,iteration:int,category:str): +def helper_generate_bipartite_graph_base64(graph,iteration:int,category:str,text:str=None): #logger.info(f'**********\nRunning helper_generate_bipartite_graph_base64\n**********') plt.figure() plt.title('Agent-Item Bipartite Graph', fontsize=16) - additional_text=f'category -> {category} iteration -> {iteration}' + additional_text=f'category -> {category} iteration -> {iteration}' if text is None else text plt.figtext(0.5, 0.85, additional_text, wrap=True, horizontalalignment='center', fontsize=10) try: top_nodes = {n for n, d in graph.nodes(data=True) if d['bipartite'] == 0} @@ -1319,19 +1317,36 @@ def helper_get_logs(log_stream): if __name__ == "__main__": import doctest, sys #print("\n", doctest.testmod(), "\n") + import time + + images_data = [] + + + def store_visualization(img_base64): # used to get us the images from fairpyx ! + images_data.append(img_base64) + logger.setLevel(logging.DEBUG) logger.addHandler(logging.StreamHandler()) - order=['Agent1','Agent2','Agent3','Agent4'] - items=['m1','m2','m3','m4'] - item_categories = {'c1': ['m1', 'm2','m3'],'c2':['m4']} - agent_category_capacities = {'Agent1': {'c1':3,'c2':2}, 'Agent2': {'c1':3,'c2':2},'Agent3': {'c1':3,'c2':2},'Agent4': {'c1':3,'c2':2}} # in the papers its written capacity=size(catergory) - valuations = {'Agent1':{'m1':2,'m2':1,'m3':1,'m4':10},'Agent2':{'m1':1,'m2':2,'m3':1,'m4':10},'Agent3':{'m1':1,'m2':1,'m3':2,'m4':10},'Agent4':{'m1':1,'m2':1,'m3':1,'m4':10}} + order= ['Agent10', 'Agent8', 'Agent5', 'Agent7', 'Agent2', 'Agent9', 'Agent4', 'Agent3', 'Agent1', 'Agent6'] + items=['m1'] + item_categories = {'c1': ['m1'], 'c2': [], 'c3': [], 'c4': [], 'c5': [], 'c6': [], 'c7': [], 'c8': [], 'c9': [], 'c10': []} + agent_category_capacities = {'Agent1': {'c1': 20, 'c2': 15, 'c3': 8, 'c4': 1, 'c5': 2, 'c6': 10, 'c7': 1, 'c8': 11, 'c9': 4, 'c10': 12}, 'Agent2': {'c1': 19, 'c2': 3, 'c3': 1, 'c4': 1, 'c5': 5, 'c6': 6, 'c7': 7, 'c8': 9, 'c9': 18, 'c10': 16}, 'Agent3': {'c1': 5, 'c2': 10, 'c3': 11, 'c4': 2, 'c5': 2, 'c6': 8, 'c7': 10, 'c8': 4, 'c9': 7, 'c10': 12}, 'Agent4': {'c1': 15, 'c2': 19, 'c3': 1, 'c4': 15, 'c5': 4, 'c6': 13, 'c7': 11, 'c8': 12, 'c9': 5, 'c10': 7}, 'Agent5': {'c1': 5, 'c2': 16, 'c3': 4, 'c4': 13, 'c5': 5, 'c6': 9, 'c7': 15, 'c8': 16, 'c9': 4, 'c10': 16}, 'Agent6': {'c1': 14, 'c2': 17, 'c3': 18, 'c4': 6, 'c5': 10, 'c6': 4, 'c7': 1, 'c8': 6, 'c9': 1, 'c10': 18}, 'Agent7': {'c1': 19, 'c2': 5, 'c3': 3, 'c4': 17, 'c5': 4, 'c6': 3, 'c7': 11, 'c8': 14, 'c9': 17, 'c10': 8}, 'Agent8': {'c1': 10, 'c2': 1, 'c3': 11, 'c4': 19, 'c5': 12, 'c6': 3, 'c7': 3, 'c8': 4, 'c9': 4, 'c10': 19}, 'Agent9': {'c1': 15, 'c2': 4, 'c3': 18, 'c4': 19, 'c5': 15, 'c6': 10, 'c7': 2, 'c8': 5, 'c9': 11, 'c10': 12}, 'Agent10': {'c1': 9, 'c2': 12, 'c3': 3, 'c4': 20, 'c5': 17, 'c6': 1, 'c7': 1, 'c8': 7, 'c9': 20, 'c10': 15}} + valuations = {'Agent1': {'m1': 100}, 'Agent2': {'m1': 100}, 'Agent3': {'m1': 100}, 'Agent4': {'m1': 100}, 'Agent5': {'m1': 100}, 'Agent6': {'m1': 100}, 'Agent7': {'m1': 100}, 'Agent8': {'m1': 100}, 'Agent9': {'m1': 100}, 'Agent10': {'m1': 100}} sum_agent_category_capacities={agent:sum(cap.values()) for agent,cap in agent_category_capacities.items()} instance=Instance(valuations=valuations,items=items,agent_capacities=sum_agent_category_capacities) - print(instance) - divide(algorithm=per_category_round_robin,instance=instance,item_categories=item_categories,agent_category_capacities=agent_category_capacities,initial_agent_order=order) + #print(instance) + start_time=time.perf_counter() + divide(algorithm=per_category_capped_round_robin,instance=instance,item_categories=item_categories,agent_category_capacities=agent_category_capacities,initial_agent_order=order) + end_time=time.perf_counter() + print(f'time taken{end_time-start_time}') + # with callback (storing graph images) + start_time = time.perf_counter() + divide(algorithm=per_category_capped_round_robin, instance=instance, item_categories=item_categories, + agent_category_capacities=agent_category_capacities, initial_agent_order=order,callback=store_visualization) + end_time = time.perf_counter() + print(f'time taken{end_time - start_time}') # divide(algorithm=two_categories_capped_round_robin,instance=instance,item_categories=item_categories,agent_category_capacities=agent_category_capacities,initial_agent_order=order,target_category_pair=("c1","c2")) # # items=['m1','m2','m3'] From 52d98e575f32a986f8bf9b46a788d9b650a36f06 Mon Sep 17 00:00:00 2001 From: Abodi Massarwa Date: Tue, 20 Aug 2024 04:06:57 +0300 Subject: [PATCH 34/36] minor changes --- ...eterogeneous_matroid_constraints_algorithms.py | 15 +++++++++++---- ...eneous_matroid_constraints_algorithms_utils.py | 11 ++++++++--- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/experiments/compare_heterogeneous_matroid_constraints_algorithms.py b/experiments/compare_heterogeneous_matroid_constraints_algorithms.py index a7a6169..ab9ed63 100644 --- a/experiments/compare_heterogeneous_matroid_constraints_algorithms.py +++ b/experiments/compare_heterogeneous_matroid_constraints_algorithms.py @@ -1,3 +1,5 @@ +import experiments_csv + from fairpyx.algorithms.fractional_egalitarian import fractional_egalitarian_allocation from fairpyx.algorithms.heterogeneous_matroid_constraints_algorithms import * from fairpyx.utils.test_heterogeneous_matroid_constraints_algorithms_utils import * @@ -102,13 +104,14 @@ def run_experiment(equal_capacities:bool,equal_valuations:bool,binary_valuations capped_round_robin: {'alloc', 'item_categories', 'agent_category_capacities', 'initial_agent_order', 'target_category'}, two_categories_capped_round_robin: {'alloc', 'item_categories', 'agent_category_capacities', 'initial_agent_order','target_category_pair'}, + per_category_capped_round_robin: {'alloc', 'agent_category_capacities', 'item_categories', 'initial_agent_order'}, iterated_priority_matching: {'alloc', 'item_categories', 'agent_category_capacities'}, egalitarian_algorithm:{'instance'}, utilitarian_algorithm:{'instance'}, iterated_maximum_matching:{'alloc'} } - + #print(f'algorithm{algorithm.__name__} , binary valuations ->{binary_valuations}') instance, agent_category_capacities, categories, initial_agent_order = random_instance( equal_capacities=equal_capacities, equal_valuations=equal_valuations, @@ -134,6 +137,7 @@ def run_experiment(equal_capacities:bool,equal_valuations:bool,binary_valuations current_algorithm_bundle_sum,current_algorithm_bundle_min_value = utilitarian_algorithm(instance) # our algorithm else:# one of our algorithms then ! + print(f'filtered kwargs->{filtered_kwargs["alloc"].instance._valuations}') algorithm(**filtered_kwargs) current_algorithm_bundle_min_value=min(alloc.agent_bundle_value(agent,bundle) for agent,bundle in alloc.bundles.items())# to compare with egalitarian algorithm current_algorithm_bundle_sum=sum(alloc.agent_bundle_value(agent,bundle)for agent,bundle in alloc.bundles.items())# to compare with utilitarian @@ -176,8 +180,11 @@ def egalitarian_algorithm(instance): for item in range(len(instance.items)) ] for agent in range(len(instance.agents)) - ] - min_egalitarian_algorithm_value = min(not_rounded_egalitarian_valuations_matrix) # egalitarian value + ]# this gives us the matrix of allocation , now search for the minimum in a matrix + # Flatten the matrix and then find the minimum value + min_egalitarian_algorithm_value = min( + min(row) for row in not_rounded_egalitarian_valuations_matrix + ) total_sum = sum(sum(row) for row in not_rounded_egalitarian_valuations_matrix) # sum of bundles (for the sake of comparison with utilitarian algorithm) return total_sum, min_egalitarian_algorithm_value @@ -185,7 +192,7 @@ def egalitarian_algorithm(instance): if __name__ == '__main__': #experiments_csv.logger.setLevel(logging.INFO) - compare_heterogeneous_matroid_constraints_algorithms_egalitarian_utilitarian() + #compare_heterogeneous_matroid_constraints_algorithms_egalitarian_utilitarian() experiments_csv.single_plot_results('results/egalitarian_utilitarian_comparison_heterogeneous_constraints_algorithms_bigData.csv',filter={},x_field='num_of_agents',y_field='current_algorithm_bundle_min_value',z_field='algorithm',save_to_file='results/egalitarian_comparison_heterogeneous_constraints_algorithms_bigData.png') # egalitarian ratio plot experiments_csv.single_plot_results('results/egalitarian_utilitarian_comparison_heterogeneous_constraints_algorithms_bigData.csv',filter={},x_field='num_of_agents',y_field='current_algorithm_bundle_sum',z_field='algorithm',save_to_file='results/utilitarian_comparison_heterogeneous_constraints_algorithms_bigData.png') # utilitarian ratio plot experiments_csv.single_plot_results('results/egalitarian_utilitarian_comparison_heterogeneous_constraints_algorithms_bigData.csv',filter={},x_field='num_of_agents',y_field='runtime',z_field='algorithm',save_to_file='results/runtime_comparison_heterogeneous_constraints_algorithms_bigData.png') # runtime plot diff --git a/fairpyx/utils/test_heterogeneous_matroid_constraints_algorithms_utils.py b/fairpyx/utils/test_heterogeneous_matroid_constraints_algorithms_utils.py index f707067..077ea33 100644 --- a/fairpyx/utils/test_heterogeneous_matroid_constraints_algorithms_utils.py +++ b/fairpyx/utils/test_heterogeneous_matroid_constraints_algorithms_utils.py @@ -123,13 +123,18 @@ def random_uniform_extended(num_of_agents: int, num_of_items: int, category in categories} # this is going to be assigned to each agent agent_category_capacities = {agent: category_capacities for agent in result_instance.agents} #✅ if equal_valuations: - random_valuation = np.random.uniform(low=item_base_value_bounds[0], high=item_base_value_bounds[1] + 1, + random_valuation = np.random.randint(low=item_base_value_bounds[0], high=item_base_value_bounds[1] + 1, size=num_of_items) + print(f"random valuation is -> {random_valuation}") # we need to normalize is sum_of_valuations = np.sum(random_valuation) normalized_sum_of_values=sum_of_valuations if item_base_value_bounds == (0, 1) else normalized_sum_of_values - normalized_random_values = np.round(random_valuation * normalized_sum_of_values / sum_of_valuations).astype( - int) # change to float if needed + if item_base_value_bounds != (0, 1): + normalized_random_values = np.round(random_valuation * normalized_sum_of_values / sum_of_valuations).astype( + int) # change to float if needed + else:#binary valuations , no need for normalization ! since we are constrained + normalized_random_values = np.round(random_valuation).astype( + int) # change to float if needed normalized_random_agent_item_valuation = { agent: dict(zip(result_instance.items, normalized_random_values From 0d7853aac39830c3324497e9971bc9adbb636b34 Mon Sep 17 00:00:00 2001 From: Abodi Massarwa Date: Tue, 20 Aug 2024 04:08:09 +0300 Subject: [PATCH 35/36] minor changes --- .../test_heterogeneous_matroid_constraints_algorithms_utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/fairpyx/utils/test_heterogeneous_matroid_constraints_algorithms_utils.py b/fairpyx/utils/test_heterogeneous_matroid_constraints_algorithms_utils.py index 077ea33..aa575bb 100644 --- a/fairpyx/utils/test_heterogeneous_matroid_constraints_algorithms_utils.py +++ b/fairpyx/utils/test_heterogeneous_matroid_constraints_algorithms_utils.py @@ -177,7 +177,6 @@ def random_uniform_extended(num_of_agents: int, num_of_items: int, result_instance = Instance(valuations=normalized_random_agent_item_valuation, item_capacities=item_capacities, agent_capacities=result_instance.agent_capacity) - #experiments_csv.logger.info(f'valuations of instance are -> {normalized_random_agent_item_valuation}') return result_instance, agent_category_capacities, categories, initial_agent_order #✅#✅#✅ From 55da4fe0026bcbf28c441589b03f22bdaff35545 Mon Sep 17 00:00:00 2001 From: Abodi Massarwa Date: Tue, 20 Aug 2024 17:41:02 +0300 Subject: [PATCH 36/36] minor changes --- ...ogeneous_matroid_constraints_algorithms.py | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/experiments/compare_heterogeneous_matroid_constraints_algorithms.py b/experiments/compare_heterogeneous_matroid_constraints_algorithms.py index ab9ed63..73a1af0 100644 --- a/experiments/compare_heterogeneous_matroid_constraints_algorithms.py +++ b/experiments/compare_heterogeneous_matroid_constraints_algorithms.py @@ -163,32 +163,39 @@ def utilitarian_algorithm(instance): def egalitarian_algorithm(instance): - # Egalitarian algorithm # Step 1: Form the valuation matrix valuation_matrix = [ [instance.agent_item_value(agent, item) for item in instance.items] for agent in instance.agents ] + # Step 2: Compute the fractional egalitarian allocation not_rounded_egal = fractional_egalitarian_allocation( Instance(valuation_matrix), normalize_utilities=False ) + # Step 3: Multiply the fractions by the original valuation matrix - not_rounded_egalitarian_valuations_matrix = [ + not_rounded_egalitarian_bundle_matrix = [ [ not_rounded_egal[agent][item] * valuation_matrix[agent][item] for item in range(len(instance.items)) ] for agent in range(len(instance.agents)) - ]# this gives us the matrix of allocation , now search for the minimum in a matrix - # Flatten the matrix and then find the minimum value - min_egalitarian_algorithm_value = min( - min(row) for row in not_rounded_egalitarian_valuations_matrix - ) - total_sum = sum(sum(row) for row in not_rounded_egalitarian_valuations_matrix) # sum of bundles (for the sake of comparison with utilitarian algorithm) + ] - return total_sum, min_egalitarian_algorithm_value + # Step 4: Calculate the total value each agent receives from their allocation + agent_total_values = [ + sum(not_rounded_egalitarian_bundle_matrix[agent]) + for agent in range(len(instance.agents)) + ] + + # Step 5: Find the minimum value among these totals + min_egalitarian_algorithm_value = min(agent_total_values) + # Step 6: Calculate the total sum of all allocations (for comparison) + total_sum = sum(agent_total_values) + + return total_sum, min_egalitarian_algorithm_value if __name__ == '__main__': #experiments_csv.logger.setLevel(logging.INFO)