Skip to content

Commit

Permalink
Merge pull request #17 from The-Firefighters/master
Browse files Browse the repository at this point in the history
Final changes to the FireFighter Algorithms
  • Loading branch information
erelsgl authored Sep 1, 2024
2 parents 4e5ed2c + 2c53fbc commit e2af4e8
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 63 deletions.
68 changes: 54 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

NetworkZ is a library of graph algorithms in Python. It is an extension of the [NetworkX](https://github.com/networkx/networkx). It contains (by import) everything that is in NetworkX, plus some additional algorithms that were submitted into NetworkX but not merged yet. Currently, NetworkZ contains the following additional algorithms:

* [Rank-maximal matching](networkz/algorithms/bipartite/rank_maximal_matching.py): by Oriya Alperin, Liel Vaknin and Amiel Lejzor.
* [Maximum-weight fractional matching](networkz/algorithms/max_weight_fractional_matching.py): by Oriya Alperin, Liel Vaknin and Amiel Lejzor.
* [Social-aware coalition formation](networkz/algorithms/approximation/coalition_formation.py) - by Victor Kushnir.
* [Minimum cut on a graph with node capacity](networkz/algorithms/max_flow_with_node_capacity.py): by Yuval Bubnovsky, Almog David and Shaked Levi.
* [Several approximate solutions to the Firefighter problem](networkz/algorithms/approximation/firefighter_problem): by Yuval Bubnovsky, Almog David and Shaked Levi.
- [Rank-maximal matching](networkz/algorithms/bipartite/rank_maximal_matching.py): by Oriya Alperin, Liel Vaknin and Amiel Lejzor.
- [Maximum-weight fractional matching](networkz/algorithms/max_weight_fractional_matching.py): by Oriya Alperin, Liel Vaknin and Amiel Lejzor.
- [Social-aware coalition formation](networkz/algorithms/approximation/coalition_formation.py) - by Victor Kushnir.
- [Minimum cut on a graph with node capacity](networkz/algorithms/max_flow_with_node_capacity.py): by Yuval Bubnovsky, Almog David and Shaked Levi.
- [Several approximate solutions to the Firefighter problem](networkz/algorithms/approximation/firefighter_problem): by Yuval Bubnovsky, Almog David and Shaked Levi.

## Installation

Expand All @@ -16,13 +16,13 @@ pip install networkz

This installs the latest version of networkx, and the new algorithms added in networkz.


## Usage

### Rank Maximal Matching

A rank-maximal matching is a matching that maximizes the number of agents who are matched to their 1st priority; subject to that, it maximizes the number of agents matched to their 2nd priority; and so on.

```
```python
import networkz as nx
G = nx.Graph()
G.add_nodes_from(["agent1", "agent2"], bipartite=0)
Expand All @@ -34,11 +34,11 @@ print(matching)

See [demo website](https://rmm.csariel.xyz/) for more information.


### Maximum-Weight Fractional Matching

Maximum-weight fractional matching is a graph optimization problem where the goal is to find a set of edges with maximum total weight, allowing for fractional inclusion of edges.

```
```python
import networkz as nx
G = nx.Graph()
G.add_nodes_from(["a1", "a2"])
Expand All @@ -47,18 +47,58 @@ F = nx.maximum_weight_fractional_matching(G)
print(F)
```

### Social-Aware Coalition Formation
### Approximating The Fire-Fighter Problem

(TODO)
The Firefighter problem models the case where a diffusive process such as an infection (or an idea, a computer virus, a fire) is spreading through a network, and our goal is to contain this infection by using targeted vaccinations.

Networkz implements several algorithms to approximate solutions for the fire-fighter problems in 2 models: spreading & non-spreading, where the virus/fire always spreads but the spread of vaccination is dependant on the model. Under each such model, we are intrested in two problem types: MaxSave (save as many nodes from the target list given a budget) and MinBudget (What is the minimum budget needed to save all of the node target list)

## Contribution
```python
import networkz as nx
G = nx.DiGraph()
G.add_nodes_from([0,1,2,3,4,5,6])
G.add_edges_from([(0,1),(0,2),(1,2),(1,4),(2,3),(2,6),(3,5)])

Any additions or bug-fixes to `networkx` should first be submitted there, according to the [NetworkX Contributor Guide](https://github.com/networkx/networkx/blob/main/CONTRIBUTING.rst).
strategy, saved_nodes = nx.spreading_maxsave(G, budget = 1, source = 0, targets = [1,2,3,4,5,6])
# Will return ([(2, 1), (4, 2)], {2, 3, 4, 5, 6})

If the pull-request is not handled, you are welcome to submit it here too.
min_budget, strategy = nx.spreading_minbudget(G, source = 0,targets = [1,2,3,4,5,6])
min_budget, strategy = nx.non_spreading_minbudget(G, source = 0,targets = [1,2,3,4,5,6])
# Both will return (2, [(1, 1), (2, 1)])
```

Another algorithm which is implemented in networkz is MinBudget in a non-spreading model (vaccine doesn't spread), running on a directed-layered network graph:

```python
import networkz as nx
G = nx.DiGraph()
G.add_nodes_from([0,1,2,3,4,5])
G.add_edges_from([(0,1),(0,2),(1,3),(1,4),(1,5),(2,3),(2,4),(2,5),(3,5),(4,5)])

# The algorithm will check if this is actually a directed-layered network graph
min_budget, strategy = nx.non_spreading_dirlaynet_minbudget(G, source = 0,targets = [1,2,3,4,5])
# Will return (2, [(1, 1), (2, 1)])
```

The library also implements two local-search based heuristic algorithms (MaxSave & MinBudget), which are applicable to both spreading models and may return better results under certain conditions: MaxSave may save more nodes on dense graph (>0.5 edge probability), and MinBudget may return a smaller budget for sparser graphs (<0.5 edge probability)

```python
import networkz as nx
G = nx.DiGraph()
G.add_nodes_from([0, 1, 2, 3])
G.add_edges_from([(0, 1), (0, 2), (1, 2), (1, 3)])
strategy, saved_nodes = nx.heuristic_maxsave(G, budget = 1, source = 0, targets = [1, 2, 3], spreading = False) # ([(1, 1)], {1, 3})
min_budget, strategy = nx.heuristic_minbudget(G, source = 0, targets = [1, 2, 3], spreading = True) # (2, [(1, 1), (2, 1)])
```

See the [algorithms in actions](https://the-firefighters.github.io/WebsiteGit/) for more information and a virtual sandbox to run and observe the algirithms.

### Social-Aware Coalition Formation

(TODO)

## Contribution

Any additions or bug-fixes to `networkx` should first be submitted there, according to the [NetworkX Contributor Guide](https://github.com/networkx/networkx/blob/main/CONTRIBUTING.rst).

If the pull-request is not handled, you are welcome to submit it here too.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,8 @@
from networkz.algorithms.max_flow_with_node_capacity import min_cut_with_node_capacity
from networkz.algorithms.approximation.firefighter_problem.Utils import *

def setup_logger(logger):
logger.setLevel(logging.DEBUG)

console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)

formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
console_handler.setFormatter(formatter)

logger.addHandler(console_handler)
return logger

logger = logging.getLogger('firefighter_problem_main')

logger = logging.getLogger(__name__)

def spreading_maxsave(Graph:nx.DiGraph, budget:int, source:int, targets:list, stop_condition=None) -> tuple[list, set]:
"""
Expand Down Expand Up @@ -339,12 +327,10 @@ def non_spreading_dirlaynet_minbudget(Graph:nx.DiGraph, source:int, targets:list
logger.error("The graph is not a DAG graph, thus cannot run the algorithm")
return

#display_graph(Graph)
logger.info(f"Starting the non_spreading_dirlaynet_minbudget function with source node {source} and targets: {targets}")

layers = adjust_nodes_capacity(Graph, source)
G = create_st_graph(Graph, targets, 't')
#display_graph(G)
G_reduction_min_cut = min_cut_with_node_capacity(G, source=source, target='t')
N_groups = min_cut_N_groups(G_reduction_min_cut,layers)
vacc_matrix = calculate_vaccine_matrix(layers, N_groups)
Expand Down Expand Up @@ -400,7 +386,6 @@ def heuristic_maxsave(Graph:nx.DiGraph, budget:int, source:int, targets:list, sp
logger.info(f"Starting the heuristic_maxsave function with source node {source}, budget {budget}, targets: {targets}, and spreading: {spreading}")

clean_graph(Graph)
#display_graph(Graph)
local_targets = targets.copy()
infected_nodes = []
vaccinated_nodes = []
Expand All @@ -409,7 +394,6 @@ def heuristic_maxsave(Graph:nx.DiGraph, budget:int, source:int, targets:list, sp
can_spread = True
Graph.nodes[source]['status'] = Status.INFECTED.value
infected_nodes.append(source)
#display_graph(Graph)
time_step = 1

while can_spread:
Expand Down
33 changes: 1 addition & 32 deletions networkz/algorithms/approximation/firefighter_problem/Utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,7 @@ class Status(Enum):
VACCINATED = "vaccinated"
DIRECTLY_VACCINATED = "directly vaccinated"


node_colors = {
'vulnerable': 'gray',
'infected': 'red',
'vaccinated': 'blue',
'directly vaccinated': 'green',
'default' : "#00FFD0"
}
logger = logging.getLogger('firefighter_problem_main')
logger = logging.getLogger('firefighter_problem')

# ============================ Validation Functions ============================

Expand Down Expand Up @@ -367,7 +359,6 @@ def spread_virus(graph:nx.DiGraph, infected_nodes:list) -> bool:
logger.debug("SPREAD VIRUS: Node " + f'{neighbor}' + " has been infected from node " + f'{node}')

infected_nodes.clear()
#display_graph(graph)
for node in new_infected_nodes:
infected_nodes.append(node)
return bool(infected_nodes)
Expand Down Expand Up @@ -403,7 +394,6 @@ def spread_vaccination(graph:nx.DiGraph, vaccinated_nodes:list) -> None:
logger.debug("SPREAD VACCINATION: Node " + f'{neighbor}' + " has been vaccinated from node " + f'{node}')

vaccinated_nodes.clear()
#display_graph(graph)
for node in new_vaccinated_nodes:
vaccinated_nodes.append(node)
return
Expand All @@ -429,7 +419,6 @@ def vaccinate_node(graph:nx.DiGraph, node:int) -> None:
"""
graph.nodes[node]['status'] = Status.DIRECTLY_VACCINATED.value
logger.info("Node " + f'{node}' + " has been directly vaccinated")
#display_graph(graph)
return

def clean_graph(graph:nx.DiGraph) -> None:
Expand Down Expand Up @@ -534,7 +523,6 @@ def create_st_graph(graph:nx.DiGraph, targets:list, new_target:str) -> nx.DiGrap
G.add_node(new_target, status = Status.VULNERABLE.value)
for node in targets:
G.add_edge(node, new_target)
#display_graph(G)

logger.info(f"Done creating a s-t graph")
return G
Expand Down Expand Up @@ -815,25 +803,6 @@ def find_best_neighbor(graph: nx.DiGraph, infected_nodes: list, remaining_target
# =========================== End Heuristic Utilities ================================

# =========================== General Utilities ======================================
def display_graph(graph:nx.DiGraph) -> None:
"""
Display the graph using Matplotlib.
Parameters
----------
graph : nx.DiGraph
Directed graph.
"""
import matplotlib.pyplot as plt
pos = nx.shell_layout(graph)
colors = [node_colors.get(data.get('status', 'default'), 'default') for node, data in graph.nodes(data=True)]
nx.draw(graph, pos, node_color=colors, with_labels=True, font_weight='bold')

if nx.get_edge_attributes(graph, 'weight'):
edge_labels = nx.get_edge_attributes(graph, 'weight')
nx.draw_networkx_edge_labels(graph, pos, edge_labels=edge_labels)
plt.show()
return

def parse_json_to_networkx(json_data):
"""
Expand Down

0 comments on commit e2af4e8

Please sign in to comment.