From 2993bf6f3aaa7a52385064c82437ba73fc60e9dd Mon Sep 17 00:00:00 2001 From: oriyalp Date: Thu, 2 Nov 2023 01:22:29 +0200 Subject: [PATCH 01/11] Add Maximum-Weight Fractional Matching algorithm --- .../max_weight_fractional_matching.py | 123 +++++++++++++ .../test_max_weight_fractional_matching.py | 173 ++++++++++++++++++ 2 files changed, 296 insertions(+) create mode 100644 networkz/algorithms/max_weight_fractional_matching.py create mode 100644 networkz/algorithms/tests/test_max_weight_fractional_matching.py diff --git a/networkz/algorithms/max_weight_fractional_matching.py b/networkz/algorithms/max_weight_fractional_matching.py new file mode 100644 index 0000000..652fefd --- /dev/null +++ b/networkz/algorithms/max_weight_fractional_matching.py @@ -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)))) diff --git a/networkz/algorithms/tests/test_max_weight_fractional_matching.py b/networkz/algorithms/tests/test_max_weight_fractional_matching.py new file mode 100644 index 0000000..c660c69 --- /dev/null +++ b/networkz/algorithms/tests/test_max_weight_fractional_matching.py @@ -0,0 +1,173 @@ +from random import randint + +import pytest + +np = pytest.importorskip("numpy") + +import networkz as nx +import networkz.algorithms.max_weight_fractional_matching as mw + + +def get_max_weight_frac(res, G=nx.Graph()): + return sum(frac * G.edges[edge]["weight"] for edge, frac in res.items()) + + +class TestMaximumWeightFractionalMatching: + def test_empty_graph(self): + G = nx.Graph() + res = mw.maximum_weight_fractional_matching(G) + assert {} == res + + def test_graph_without_edges(self): + G = nx.Graph() + G.add_nodes_from([i for i in range(0, 10)]) + res = mw.maximum_weight_fractional_matching(G) + assert {} == res + + def test_simple_graph_without_weights(self): + G = nx.Graph() + G.add_nodes_from(["a1", "a2"]) + G.add_edge("a1", "a2") + res = mw.maximum_weight_fractional_matching(G) + assert {("a1", "a2"): 1.0} == res + + def test_simple_graph_with_weight(self): + G = nx.Graph() + G.add_nodes_from(["a1", "a2"]) + G.add_edge("a1", "a2", weight=3) + res = mw.maximum_weight_fractional_matching(G) + assert {("a1", "a2"): 1.0} == res + + def test_simple_graph_with_negative_weight(self): + G = nx.Graph() + G.add_nodes_from(["a1", "a2"]) + G.add_edge("a1", "a2", weight=-1) + res = mw.maximum_weight_fractional_matching(G) + assert {("a1", "a2"): 0} == res + + def test_3_nodes_graph_without_weights(self): + G = nx.Graph() + G.add_nodes_from(["a1", "a2", "a3"]) + G.add_edges_from([("a1", "a2"), ("a1", "a3"), ("a2", "a3")]) + res = mw.maximum_weight_fractional_matching(G) + max_weight = np.round(sum(frac for frac in res.values()), 3) + exp_val = {("a1", "a2"): 0.5, ("a1", "a3"): 0.5, ("a2", "a3"): 0.5} + weight = np.round(sum(frac for frac in exp_val.values()), 3) + assert weight == max_weight + + def test_simple_graph_with_equal_weights(self): + G = nx.Graph() + G.add_nodes_from(["a1", "a2", "a3"]) + G.add_weighted_edges_from([("a1", "a2", 5), ("a1", "a3", 5), ("a2", "a3", 5)]) + res = mw.maximum_weight_fractional_matching(G) + max_weight = get_max_weight_frac(res, G) + exp_val = {("a1", "a2"): 0.5, ("a1", "a3"): 0.5, ("a2", "a3"): 0.5} + weight = get_max_weight_frac(exp_val, G) + assert weight == max_weight + + def test_3_nodes_graph_1_3_weights(self): + G = nx.Graph() + G.add_nodes_from(["a1", "a2", "a3"]) + G.add_weighted_edges_from([("a1", "a2", 1), ("a1", "a3", 2), ("a2", "a3", 3)]) + res = mw.maximum_weight_fractional_matching(G) + max_weight = get_max_weight_frac(res, G) + exp_val = {("a1", "a2"): 0.5, ("a1", "a3"): 0.5, ("a2", "a3"): 0.5} + weight = get_max_weight_frac(exp_val, G) + assert weight == max_weight + + def test_3_nodes_graph_with_weights(self): + G = nx.Graph() + G.add_nodes_from(["a1", "a2", "a3"]) + G.add_weighted_edges_from([("a1", "a2", 1), ("a1", "a3", 2), ("a2", "a3", 4)]) + res = mw.maximum_weight_fractional_matching(G) + max_weight = get_max_weight_frac(res, G) + exp_val = {("a1", "a2"): 0.0, ("a1", "a3"): 0.0, ("a2", "a3"): 1.0} + weight = get_max_weight_frac(exp_val, G) + assert weight == max_weight + + def test_3_nodes_graph_with_negative_weight(self): + G = nx.Graph() + G.add_nodes_from(["a1", "a2", "a3"]) + G.add_weighted_edges_from([("a1", "a2", -1), ("a1", "a3", 5), ("a2", "a3", 10)]) + res = mw.maximum_weight_fractional_matching(G) + max_weight = get_max_weight_frac(res, G) + exp_val = {("a1", "a2"): 0.0, ("a1", "a3"): 0.0, ("a2", "a3"): 1.0} + weight = get_max_weight_frac(exp_val, G) + assert weight == max_weight + + def test_7_nodes_graph_without_weights(self): + G = nx.Graph() + G.add_nodes_from([i for i in range(0, 7)]) + G.add_edges_from( + [(0, 1), (0, 2), (0, 6), (0, 5), (1, 6), (2, 3), (2, 4), (2, 6), (3, 4)] + ) + res = mw.maximum_weight_fractional_matching(G) + max_weight = np.round(sum(frac for frac in res.values()), 3) + exp_val = { + (0, 1): 0.0, + (0, 2): 0.0, + (0, 6): 0.0, + (0, 5): 1.0, + (1, 6): 1.0, + (2, 3): 0.5, + (2, 4): 0.5, + (2, 6): 0.0, + (3, 4): 0.5, + } + weight = np.round(sum(frac for frac in exp_val.values()), 3) + assert weight == max_weight + + def test_7_nodes_graph_with_weights(self): + G = nx.Graph() + G.add_nodes_from([i for i in range(0, 7)]) + G.add_weighted_edges_from( + [ + (0, 1, 12), + (0, 2, 7), + (0, 6, 21), + (0, 5, 6), + (1, 6, 1), + (2, 3, 23), + (2, 4, 5), + (2, 6, 8), + (3, 4, 19), + ] + ) + res = mw.maximum_weight_fractional_matching(G) + max_weight = get_max_weight_frac(res, G) + exp_val = { + (0, 1): 0.0, + (0, 2): 0.0, + (0, 6): 1.0, + (0, 5): 0.0, + (1, 6): 0.0, + (2, 3): 0.5, + (2, 4): 0.5, + (2, 6): 0.0, + (3, 4): 0.5, + } + weight = get_max_weight_frac(exp_val, G) + assert weight == max_weight + + def test_completes_graph_without_weights(self): + for i in range(0, 5): + n = randint(2, 30) + G = nx.complete_graph(n) + res = mw.maximum_weight_fractional_matching(G) + max_weight = np.round(sum(frac for frac in res.values()), 3) + weight = np.round((1 / (n - 1)) * len(G.edges()), 3) + assert weight == max_weight + + def test_sum_of_weights_in_every_node_in_random_graph_is_less_than_1(self): + seed = 1 + for i in range(0, 5): + G = nx.random_regular_graph(4, 6, seed) + res = mw.maximum_weight_fractional_matching(G) + for node in G.nodes: + sum_adj = 0 + for edge in G.edges(node): + if edge in res: + sum_adj += res[edge] + else: + sum_adj += res[(edge[1], edge[0])] + assert sum_adj == 1 From 47245b284621ca9da8bab25d7b3569ab4f1bc942 Mon Sep 17 00:00:00 2001 From: oriyalp Date: Thu, 2 Nov 2023 01:45:48 +0200 Subject: [PATCH 02/11] Import max-weight fractional matching --- networkz/algorithms/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/networkz/algorithms/__init__.py b/networkz/algorithms/__init__.py index 5f9af6d..031119f 100644 --- a/networkz/algorithms/__init__.py +++ b/networkz/algorithms/__init__.py @@ -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 From 251e28f399fd2c9f17a0fee8b2b421c2191b1502 Mon Sep 17 00:00:00 2001 From: oriyalperin Date: Thu, 2 Nov 2023 10:37:26 +0200 Subject: [PATCH 03/11] Update settings --- .github/workflows/test.yml | 4 +- networkz/__init__.py | 5 +- pyproject.toml | 104 +++++++++++++++++++++++++++++++++++-- requirements/default.txt | 3 +- 4 files changed, 108 insertions(+), 8 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d78ee93..ebb5036 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -29,7 +29,7 @@ jobs: - name: Test NetworkZ run: | - pytest --durations=10 + pytest --durations=10 --pyargs networkz default: runs-on: ${{ matrix.os }}-latest @@ -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: diff --git a/networkz/__init__.py b/networkz/__init__.py index 0c3469b..c4dc96e 100644 --- a/networkz/__init__.py +++ b/networkz/__init__.py @@ -1,6 +1,8 @@ from networkx import * import os +__version__ = "1.0.6" + # Get the current directory of the __init__.py file current_directory = os.path.dirname(__file__) @@ -8,5 +10,4 @@ __path__.append(os.path.join(current_directory, 'algorithms')) from networkz import algorithms -from networkz.algorithms import * - +from networkz.algorithms import * \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 10d94d0..608c566 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,6 +2,104 @@ requires = ["setuptools>=46.4.0", "wheel"] build-backend = "setuptools.build_meta" -[tool.pytest.ini_options] -minversion = "6.0" -addopts = "--doctest-modules " \ No newline at end of file +[project] +name = 'networkz' +description = 'Extended Graph-Algorithms Library on Top of NetworkX' +readme = 'README.MD' +requires-python = '>=3.8' +dynamic = ['version'] +keywords = [ + 'Networks', + 'Graph Theory', + 'Mathematics', + 'network', + 'graph', + 'discrete mathematics', + 'math', +] + +dependencies = [ + "networkx", +] + +[[project.authors]] +name = 'Ariel University' +email = 'networkz@csariel.xyz' + +[[project.maintainers]] +name = 'NetworkZ Developers' +email = 'networkz-discuss@googlegroups.com' + +[project.urls] +"Source Code" = 'https://github.com/ariel-research/networkz' + +[project.optional-dependencies] +default = [ + 'networkx', + 'numpy>=1.22', + 'scipy>=1.9,!=1.11.0,!=1.11.1', +] +developer = [ + 'changelist==0.4', + 'pre-commit>=3.2', + 'mypy>=1.1', + 'rtoml', +] +extra = [ + 'lxml>=4.6', + 'pygraphviz>=1.11', + 'pydot>=1.4.2', + 'sympy>=1.10', +] +test = [ + 'pytest>=7.2', + 'pytest-cov>=4.0', +] + +[tool.setuptools] +zip-safe = false +include-package-data = false +packages = [ + 'networkz', + 'networkz.algorithms', + 'networkz.algorithms.bipartite', + 'networkz.algorithms.approximation', + 'networkz.classes', + 'networkz.generators', + 'networkz.drawing', + 'networkz.linalg', + 'networkz.readwrite', + 'networkz.tests', + 'networkz.utils', +] +platforms = [ + 'Linux', + 'Mac OSX', + 'Windows', + 'Unix', +] + +[tool.setuptools.dynamic.version] +attr = 'networkz.__version__' + + +[tool.setuptools.package-data] +networkz = ['tests/*.py'] +"networkz.algorithms" = ['tests/*.py'] +"networkz.algorithms.bipartite" = ['tests/*.py'] +"networkz.algorithms.approximation" = ['tests/*.py'] + +[tool.ruff] +line-length = 88 +target-version = 'py39' +select = [ + 'I', +] + +[tool.ruff.per-file-ignores] +"__init__.py" = ['I'] + + +[tool.mypy] +ignore_missing_imports = true +exclude = 'subgraphviews|reportviews' diff --git a/requirements/default.txt b/requirements/default.txt index 95257cb..205c33f 100644 --- a/requirements/default.txt +++ b/requirements/default.txt @@ -1,2 +1,3 @@ networkx - +numpy>=1.22 +scipy>=1.9,!=1.11.0,!=1.11.1 From eb99d58cf7c345eb4e831a22c2f30a69e9d95557 Mon Sep 17 00:00:00 2001 From: oriyalp Date: Thu, 2 Nov 2023 16:36:14 +0200 Subject: [PATCH 04/11] Add conftest --- networkz/utils/conftest.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 networkz/utils/conftest.py diff --git a/networkz/utils/conftest.py b/networkz/utils/conftest.py new file mode 100644 index 0000000..e69de29 From 9f380876ba53df556895a222931345e486c80fcc Mon Sep 17 00:00:00 2001 From: oriyalp Date: Fri, 3 Nov 2023 09:24:27 +0200 Subject: [PATCH 05/11] Move conftest file --- networkz/conftest.py | 47 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 networkz/conftest.py diff --git a/networkz/conftest.py b/networkz/conftest.py new file mode 100644 index 0000000..adf6398 --- /dev/null +++ b/networkz/conftest.py @@ -0,0 +1,47 @@ +""" +Testing +======= + +General guidelines for writing good tests: + +- doctests always assume ``import networkx as nx`` so don't add that +- prefer pytest fixtures over classes with setup methods. +- use the ``@pytest.mark.parametrize`` decorator +- use ``pytest.importorskip`` for numpy, scipy, pandas, and matplotlib b/c of PyPy. + and add the module to the relevant entries below. + +""" + +# What dependencies are installed? + +try: + import numpy + + has_numpy = True +except ImportError: + has_numpy = False + +try: + import scipy + + has_scipy = True +except ImportError: + has_scipy = False + + + +# List of files that pytest should ignore + +collect_ignore = [] + +needs_numpy = [ + "algorithms/max_weight_fractional_matching.py", +] +needs_scipy = [ + "algorithms/max_weight_fractional_matching.py", +] + +if not has_numpy: + collect_ignore += needs_numpy +if not has_scipy: + collect_ignore += needs_scipy From ed53c2331e122ff5db0fdfc3a4fd5abb9c310cea Mon Sep 17 00:00:00 2001 From: oriyalp Date: Fri, 3 Nov 2023 09:25:03 +0200 Subject: [PATCH 06/11] Change requirements --- pyproject.toml | 17 ++++------------- requirements/default.txt | 4 +--- requirements/developer.txt | 3 +-- requirements/extra.txt | 5 +---- requirements/test.txt | 3 +-- 5 files changed, 8 insertions(+), 24 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 608c566..768c83d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,25 +35,16 @@ email = 'networkz-discuss@googlegroups.com' [project.optional-dependencies] default = [ - 'networkx', - 'numpy>=1.22', - 'scipy>=1.9,!=1.11.0,!=1.11.1', + 'networkx[default]', ] developer = [ - 'changelist==0.4', - 'pre-commit>=3.2', - 'mypy>=1.1', - 'rtoml', + 'networkx[developer], ] extra = [ - 'lxml>=4.6', - 'pygraphviz>=1.11', - 'pydot>=1.4.2', - 'sympy>=1.10', + 'networkx[extra], ] test = [ - 'pytest>=7.2', - 'pytest-cov>=4.0', + 'networkx[test], ] [tool.setuptools] diff --git a/requirements/default.txt b/requirements/default.txt index 205c33f..20272f3 100644 --- a/requirements/default.txt +++ b/requirements/default.txt @@ -1,3 +1 @@ -networkx -numpy>=1.22 -scipy>=1.9,!=1.11.0,!=1.11.1 +networkx[default] diff --git a/requirements/developer.txt b/requirements/developer.txt index fd8000c..8051f51 100644 --- a/requirements/developer.txt +++ b/requirements/developer.txt @@ -1,2 +1 @@ -pre-commit>=3.2 -mypy>=1.1 \ No newline at end of file +networkx[developer] \ No newline at end of file diff --git a/requirements/extra.txt b/requirements/extra.txt index 1369cb9..926ed1e 100644 --- a/requirements/extra.txt +++ b/requirements/extra.txt @@ -1,4 +1 @@ -lxml>=4.6 -pygraphviz>=1.11 -pydot>=1.4.2 -sympy>=1.10 \ No newline at end of file +networkx[extra] \ No newline at end of file diff --git a/requirements/test.txt b/requirements/test.txt index 7f06e89..4ff9f8e 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -1,2 +1 @@ -pytest>=7.2 -pytest-cov>=4.0 \ No newline at end of file +networkx[test] \ No newline at end of file From 79416a1fb08f813df588467bc72620843c84b8db Mon Sep 17 00:00:00 2001 From: oriyalp Date: Fri, 3 Nov 2023 09:28:24 +0200 Subject: [PATCH 07/11] Update pyproject.toml --- pyproject.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 768c83d..29efeab 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,13 +38,13 @@ default = [ 'networkx[default]', ] developer = [ - 'networkx[developer], + 'networkx[developer]', ] extra = [ - 'networkx[extra], + 'networkx[extra]', ] test = [ - 'networkx[test], + 'networkx[test]', ] [tool.setuptools] From 7d32e67e897181ed9a67a955ee1fc0dd2a2c2e4f Mon Sep 17 00:00:00 2001 From: oriyalp Date: Fri, 3 Nov 2023 21:27:12 +0200 Subject: [PATCH 08/11] Update conftest.py --- networkz/conftest.py | 16 ++++++++++++++++ networkz/utils/conftest.py | 0 2 files changed, 16 insertions(+) delete mode 100644 networkz/utils/conftest.py diff --git a/networkz/conftest.py b/networkz/conftest.py index adf6398..441743e 100644 --- a/networkz/conftest.py +++ b/networkz/conftest.py @@ -12,6 +12,22 @@ """ +import pytest + +import networkz + + +@pytest.fixture(autouse=True) +def add_nx(doctest_namespace): + doctest_namespace["nx"] = networkz + # TODO: remove the try-except block when we require numpy >= 2 + try: + import numpy as np + + np.set_printoptions(legacy="1.21") + except ImportError: + pass + # What dependencies are installed? try: diff --git a/networkz/utils/conftest.py b/networkz/utils/conftest.py deleted file mode 100644 index e69de29..0000000 From 0dba1285317cebf0b316b985db5a38856c45d79a Mon Sep 17 00:00:00 2001 From: oriyalp Date: Fri, 3 Nov 2023 21:52:16 +0200 Subject: [PATCH 09/11] comment base test --- .github/workflows/test.yml | 46 +++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ebb5036..076f238 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -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 --pyargs networkz + # 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 From 83974683f09f60f47023177c9f60051a05919201 Mon Sep 17 00:00:00 2001 From: Oriya Alperin <73301483+oriyalperin@users.noreply.github.com> Date: Thu, 9 Nov 2023 13:27:10 +0200 Subject: [PATCH 10/11] Update test.yml --- .github/workflows/test.yml | 46 +++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ebb5036..076f238 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -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 --pyargs networkz + # 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 From caa4d2ca23ce1d966dabc4c57044733be4be6b50 Mon Sep 17 00:00:00 2001 From: Oriya Alperin <73301483+oriyalperin@users.noreply.github.com> Date: Sun, 12 Nov 2023 09:19:23 +0200 Subject: [PATCH 11/11] Update README.md add maximum-weight fractional matching --- README.md | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 11d1a2d..5313b1e 100644 --- a/README.md +++ b/README.md @@ -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. ``` @@ -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)