Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
oriyalperin committed May 19, 2024
2 parents da0cf62 + caa4d2c commit 6302437
Show file tree
Hide file tree
Showing 12 changed files with 498 additions and 41 deletions.
48 changes: 24 additions & 24 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,29 @@ concurrency:
cancel-in-progress: true

jobs:
base:
runs-on: ${{ matrix.os }}-latest
strategy:
matrix:
os: [ubuntu, macos, windows]
python-version: ["pypy-3.9"]
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}

- name: Install packages
run: |
python -m pip install --upgrade pip wheel setuptools
python -m pip install -r requirements/test.txt
python -m pip install .
python -m pip list
- name: Test NetworkZ
run: |
pytest --durations=10
# base:
# runs-on: ${{ matrix.os }}-latest
# strategy:
# matrix:
# os: [ubuntu, macos, windows]
# python-version: ["pypy-3.9"]
# steps:
# - uses: actions/checkout@v3
# - name: Set up Python ${{ matrix.python-version }}
# uses: actions/setup-python@v4
# with:
# python-version: ${{ matrix.python-version }}

# - name: Install packages
# run: |
# python -m pip install --upgrade pip wheel setuptools
# python -m pip install -r requirements/test.txt
# python -m pip install .
# python -m pip list

# - name: Test NetworkZ
# run: |
# pytest --durations=10 --pyargs networkz

default:
runs-on: ${{ matrix.os }}-latest
Expand Down Expand Up @@ -128,7 +128,7 @@ jobs:
os: [ubuntu, macos]
python-version: ["3.9", "3.10", "3.11"]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
Expand Down
17 changes: 15 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ This installs the latest version of networkx, and the new algorithms added in ne

## Usage

### Rank Maximal Matching Algorithm
### 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.

```
Expand All @@ -31,7 +31,20 @@ print(matching)

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

### Social-aware coalition formation

### 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.

```
import networkz as nx
G = nx.Graph()
G.add_nodes_from(["a1", "a2"])
G.add_edge("a1", "a2", weight=3)
F = nx.maximum_weight_fractional_matching(G)
print(F)
```

### Social-Aware Coalition Formation

(TODO)

Expand Down
5 changes: 3 additions & 2 deletions networkz/__init__.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
from networkx import *
import os

__version__ = "1.0.6"

# Get the current directory of the __init__.py file
current_directory = os.path.dirname(__file__)

# Append the 'algorithms' directory to the __path__ attribute
__path__.append(os.path.join(current_directory, 'algorithms'))

from networkz import algorithms
from networkz.algorithms import *

from networkz.algorithms import *
1 change: 1 addition & 0 deletions networkz/algorithms/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from networkx.algorithms import *
from networkz.algorithms import max_weight_fractional_matching
from networkx.algorithms import bipartite
from networkz.algorithms.bipartite import rank_maximal_matching
from networkx.algorithms import approximation
Expand Down
123 changes: 123 additions & 0 deletions networkz/algorithms/max_weight_fractional_matching.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import numpy as np
from scipy.optimize import linprog

import networkz as nx
from networkz import incidence_matrix


def maximum_weight_fractional_matching(G: nx.Graph, weight="weight", **linprog_options):
"""Returns the maximum-weight fractional matching of the wighted graph `G`.
A fractional graph is a graph in which every edge has a fraction [0,1]
such that the sum of fractions of edges adjacent to each vertex is at most 1.
A matching is a set of edges that do not share any nodes.
Define fw(e) for each edge e to be the multiplication of its weight and fraction.
A maximum-weight fractional matching is one with the maximum fw(e) sum of all e in E(G).
A fractional matching of maximum weight in a graph can be found by linear programming.
*If the edges are not weighted - the weight of each edge is 1.
Parameters
----------
G : NetworkX graph
Undirected weighted graph
weight : str
the name of the edge attribute that represents the weight of an edge.
linprog_options : dict
scipy.optimize.linprog options, None as default.
Returns
-------
F : dictionary
The fractions are returned as a dictionary, `frac`, such that
``frac[e] == f`` for edge `e` with fraction `f` (rounded to 3 decimals).
Examples
--------
In the weighted graph, G = (V,E).
>>> G = nx.Graph()
>>> G.add_nodes_from(["a1", "a2"])
>>> G.add_edge("a1", "a2", weight=3)
>>> F = maximum_weight_fractional_matching(G)
>>> print(F=={('a1', 'a2'): 1.0})
True
>>> F[('a1','a2')]
1.0
explanation: weight = 3
G = a1-----------a2
frac = 1.0
maximum_weight_fractional_matching(G) = a1-----------a2
The returned value is {('a1', 'a2'): 1.0}.
There is only one edge, so it gets the maximaum value.
another example:
>>> G = nx.Graph()
>>> G.add_nodes_from(["a1", "a2", "a3"])
>>> G.add_weighted_edges_from([("a1", "a2", 1), ("a1", "a3", 2), ("a2", "a3", 3)])
>>> F = maximum_weight_fractional_matching(G)
>>> print(F=={('a1', 'a2'): 0.5, ('a1', 'a3'): 0.5, ('a2', 'a3'): 0.5})
True
>>> F[('a2','a3')]
0.5
explanation: weight = 1
G = a1------------a2
\\ \\
weight = 2 \\ \\ weight = 3
\\ \\
\\ \\
a3
frac = 0.5
maximum_weight_fractional_matching(G) = a1------------a2
\\ \\
frac = 0.5 \\ \\ frac = 0.5
\\ \\
\\ \\
a3
The returned value is {('a1', 'a2'): 0.5, ('a1', 'a3'): 0.5, ('a2', 'a3'): 0.5}.
We want to find Max(x,y,z) S.T
a1: x +2y<=1
a2: x+3z<=1
a3: 2y+3z<=1
and
x,y,z<=1
we can solve it using the linprog function:
linprog(c, A_ub, b_ub, bounds, method='highs')
linprog solve the same problem, but it finds the Min(x,y,z) so if we want Max(x,y,z)
we can change our inqualities to be:
Min(x,y,z)
S.T
a1: x +2y>=-1
a2: x+3z>=-1
a3: 2y+3z>=-1
set bounds = (-1, 0)
and then take the result as ABS, like that - |Min(x,y,z)|
than we will get the solution for our original problem = {('a1', 'a2'): 0.5, ('a1', 'a3'): 0.5, ('a2', 'a3'): 0.5}
See Also
--------
linprog
References
----------
https://en.wikipedia.org/wiki/Fractional_matching
"""

if G.number_of_nodes() == 0 or G.number_of_edges() == 0:
return dict()
c = [G.edges[edge].get(weight, 1) for edge in G.edges]
b = [1] * len(G.nodes)
bounds = (-1, 0)
A = -incidence_matrix(G)
res = linprog(
c, A_ub=A, b_ub=b, bounds=bounds, method="highs", options=linprog_options
)
return dict(zip(G.edges, np.abs(np.round(res.x, 3))))
Loading

0 comments on commit 6302437

Please sign in to comment.