From 3654ca367e9d16b68d0a8f52fb15c88c3aea53e8 Mon Sep 17 00:00:00 2001 From: apister <alexis.pister@inria.fr> Date: Mon, 28 Jun 2021 14:58:37 +0200 Subject: [PATCH] dynCom Instant Optimal --- aleclust/clustering/clustering.py | 2 +- aleclust/globals.py | 2 +- api.py | 58 +++--- dynCom/dynCom/InstantOptimal.py | 80 +++++++++ dynCom/dynCom/dynCommunities.py | 6 +- dynCom/dynCom/temporal_trade_off.py | 2 +- dynCom/notebooks/temporal_trad_off.ipynb | 220 +++++++++++------------ dynCom/notebooks/tiles.ipynb | 160 +++++++++++++++++ dynCom/setup.py | 11 +- dynCom/tests/conftest.py | 10 +- dynCom/tests/test_InstantOptimal.py | 10 ++ dynCom/tests/test_temporal_trade_off.py | 12 +- dynCom/tests/test_utils.py | 8 +- setup.py | 4 +- 14 files changed, 418 insertions(+), 167 deletions(-) create mode 100644 dynCom/dynCom/InstantOptimal.py create mode 100644 dynCom/notebooks/tiles.ipynb create mode 100644 dynCom/tests/test_InstantOptimal.py diff --git a/aleclust/clustering/clustering.py b/aleclust/clustering/clustering.py index b0e92fc..63b24e4 100644 --- a/aleclust/clustering/clustering.py +++ b/aleclust/clustering/clustering.py @@ -17,7 +17,7 @@ import aleclust.utility as utility import aleclust.clustering.communities as nodeClustering # TODO : Move to metric -from clustering.attributeCD import attribute_clustering_preprocessing +from aleclust.clustering.attributeCD import attribute_clustering_preprocessing METRICS = { "coverage": community.quality.coverage, diff --git a/aleclust/globals.py b/aleclust/globals.py index c479f3b..b5ca9fc 100644 --- a/aleclust/globals.py +++ b/aleclust/globals.py @@ -364,7 +364,7 @@ METRICS_PARAMETERS = ["bimodularity_entity_type"] import dynCom.louvain as dynamic_louvain DYNAMIC_CDA = { - "louvain": { + "louvain_tdf": { "description": "Launch the Louvain algorithm at each snapshot, with an initial condition corresponding to the result of the previous timeslot. Use an heuristic to not find a local optimum too fast", "parameters": { "resolution": { diff --git a/api.py b/api.py index aa42f9e..e12e696 100644 --- a/api.py +++ b/api.py @@ -1,12 +1,4 @@ # -*- coding: utf-8 -*- -from flask import Flask, jsonify, request, send_file -from flask_socketio import SocketIO, emit, send, Namespace -from flask_cors import CORS -from flask import current_app - -import requests -from urllib.parse import urlparse -from io import BytesIO import os import rapidjson @@ -14,9 +6,18 @@ import copy import sys import time +from flask import Flask, jsonify, request, send_file +from flask_socketio import SocketIO, emit, send, Namespace +from flask_cors import CORS +from flask import current_app +import requests +from urllib.parse import urlparse +from io import BytesIO import networkx as nx from networkx.readwrite import json_graph +from dynCom.InstantOptimal import InstantOptimal + import aleclust.processing.preprocessing as preprocessing import aleclust.utility as utility import aleclust.dynamiClustering.dynamiClustering as dynamiClustering @@ -54,14 +55,16 @@ dataPaths = { @app.route("/", methods=["GET"]) def base(): - message = {"info": INFO, - "datasets": list(dataPaths.keys()), - "algorithms": {"unipartite": utility.dict_to_dict_of_strings( - utility.delete_keys_from_dict(copy.deepcopy(UNIPARTITE_CDA), ["function"])), - "bipartite": utility.dict_to_dict_of_strings( - utility.delete_keys_from_dict(copy.deepcopy(BIPARTITE_CDA), ["function"]))}, - "parameters": utility.dict_to_dict_of_strings(PARAMETERS), - "metrics": list(clustering.METRICS.keys())} + message = { + "info": INFO, + "datasets": list(dataPaths.keys()), + "algorithms": {"unipartite": utility.dict_to_dict_of_strings( + utility.delete_keys_from_dict(copy.deepcopy(UNIPARTITE_CDA), ["function"])), + "bipartite": utility.dict_to_dict_of_strings( + utility.delete_keys_from_dict(copy.deepcopy(BIPARTITE_CDA), ["function"]))}, + "parameters": utility.dict_to_dict_of_strings(PARAMETERS), + "metrics": list(clustering.METRICS.keys()) + } return jsonify(message) @@ -111,17 +114,21 @@ def static_clustering_request(flask_request, clustering_algo, dataName=False, dy def dynamic_clustering_request(flask_request, clustering_algo): from dynCom import json_read, louvain json = flask_request.json - json_parser = PaohJsonParser(json, dynamic=True) - parameters: Parameters = json_parser.parameters - parameters.algorithm_list = DYNAMIC_CDA - # get URL arguments - args = flask_request.args - parameters.parse_parameters(clustering_algo, args) + if clustering_algo in DYNAMIC_CDA: + json_parser = PaohJsonParser(json, dynamic=True) + parameters: Parameters = json_parser.parameters + parameters.algorithm_list = DYNAMIC_CDA - dyn_graph = json_parser.own_json_to_graph(dynamic=True) + # get URL arguments + args = flask_request.args + parameters.parse_parameters(clustering_algo, args) + dyn_graph = json_parser.own_json_to_graph(dynamic=True) - model = DYNAMIC_CDA[clustering_algo]["function"](**parameters.clustering_parameters) + model = DYNAMIC_CDA[clustering_algo]["function"](**parameters.clustering_parameters) + else: + dyn_graph = PaohJsonParser.json_to_dyn_graph(json, True) + model = InstantOptimal(clustering_algo) dyn_coms = model.apply(dyn_graph) dyn_coms_json = dyn_coms.to_json() @@ -278,4 +285,5 @@ if __name__ == "__main__": app.run(debug=debug, port=sys.argv[1]) else: # socketio.run(app, port=10080) - app.run(debug=debug, port=10080) + # app.run(debug=debug, port=10080) + app.run(debug=debug, port=10085) diff --git a/dynCom/dynCom/InstantOptimal.py b/dynCom/dynCom/InstantOptimal.py new file mode 100644 index 0000000..46a3aa4 --- /dev/null +++ b/dynCom/dynCom/InstantOptimal.py @@ -0,0 +1,80 @@ +import networkx as nx +import dynetx as dn +from cdlib import algorithms +from cdlib import TemporalClustering +from networkx.generators.community import LFR_benchmark_graph + +from dynCom import utils, dynCommunities + + +def cdlib_handler(algorithm_name): + cdlib_function = algorithms.__dict__[algorithm_name] + return cdlib_function + + +def t_com_id_to_com_id(t_com_id): + return t_com_id.split("_")[-1] + + +def jaccard(x, y): + return len(set(x) & set(y)) / len(set(x) | set(y)) + + +class InstantOptimal: + def __init__(self, alg_name): + self.alg_name = alg_name + + self.dyn_communities = None + self.temporal_clustering = None + self.snapshot_ids = None + self.matches = None + self.tcomids_to_comid = None + self.com_id = None + + def apply(self, dyn_graph, **kwargs): + self.dyn_communities = dynCommunities.DynCommunities(dyn_graph) + self.temporal_clustering = TemporalClustering() + self.snapshot_ids = dyn_graph.temporal_snapshots_ids() + + for i, snapshot_id in enumerate(self.snapshot_ids): + snapshot = utils.dyn_to_nxgraph(dyn_graph.time_slice(snapshot_id)) + partition = cdlib_handler(self.alg_name)(snapshot, **kwargs) + self.temporal_clustering.add_clustering(partition, snapshot_id) + + self.matches = self.temporal_clustering.community_matching(jaccard, two_sided=True) + self.matching_step() + + self.save_to_dyn_communities() + return self.dyn_communities + + def matching_step(self): + self.tcomids_to_comid = {} + self.com_id = 0 + for match in self.matches: + similarity = match[2] + com_id_t0 = match[0] + com_id_t1 = match[1] + + if com_id_t0 in self.tcomids_to_comid: + com_id = self.tcomids_to_comid[com_id_t0] + else: + com_id = self.com_id + self.tcomids_to_comid[com_id_t0] = com_id + self.com_id += 1 + + if similarity > 0.5: + self.tcomids_to_comid[com_id_t1] = com_id + + def save_to_dyn_communities(self): + for snapshot_id in self.snapshot_ids: + for j, com in enumerate(self.temporal_clustering.get_clustering_at(snapshot_id).communities): + t_com_id = f"{snapshot_id}_{j}" + if t_com_id in self.tcomids_to_comid: + com_id = self.tcomids_to_comid[f"{snapshot_id}_{j}"] + else: + com_id = self.com_id + self.com_id += 1 + + self.dyn_communities.communities[snapshot_id][com_id] = com + + # print(self.dyn_communities.communities) diff --git a/dynCom/dynCom/dynCommunities.py b/dynCom/dynCom/dynCommunities.py index e85a6f2..05d86ae 100644 --- a/dynCom/dynCom/dynCommunities.py +++ b/dynCom/dynCom/dynCommunities.py @@ -12,10 +12,11 @@ def dict_to_list_communities(dict_communities): class DynCommunities: def __init__(self, dyn_graph=None): self.dyn_graph = dyn_graph - self.communities: dict[str, dict[int, list]] = {} + # self.communities: dict[str, dict[int, list]] = {} # Time to com to node + self.communities: dict[str, dict[int, list]] = defaultdict(dict) # Time to com to node self.json = None - self.no_community_label = "None" + self.no_community_label = None def to_json(self): self.json = {} @@ -33,4 +34,5 @@ class DynCommunities: self.json["nodes"] = dict_to_list_communities(nodes_communities) self.json["graph"] = {} + return self.json diff --git a/dynCom/dynCom/temporal_trade_off.py b/dynCom/dynCom/temporal_trade_off.py index 60bd2fd..ea0044b 100644 --- a/dynCom/dynCom/temporal_trade_off.py +++ b/dynCom/dynCom/temporal_trade_off.py @@ -25,7 +25,7 @@ class GlobalOptUpdate: self.snapshot_ids = dyn_graph.temporal_snapshots_ids() for i, snapshot_id in enumerate(self.snapshot_ids): - print(i, snapshot_id) + # print(i, snapshot_id) snapshot = utils.dyn_to_nxgraph(dyn_graph.time_slice(snapshot_id)) if i == 0: diff --git a/dynCom/notebooks/temporal_trad_off.ipynb b/dynCom/notebooks/temporal_trad_off.ipynb index 14b80a0..229a526 100644 --- a/dynCom/notebooks/temporal_trad_off.ipynb +++ b/dynCom/notebooks/temporal_trad_off.ipynb @@ -2,61 +2,8 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1801\n", - "[(0, 1)]\n", - "[(0, 2)]\n", - "[(5, 6)]\n", - "[(5, 7)]\n", - "1802\n", - "[(0, 1)]\n", - "[(0, 2)]\n", - "[(0, 3)]\n", - "[(0, 4)]\n", - "[(4, 5)]\n", - "[(5, 6)]\n", - "[(5, 7)]\n", - "1803\n", - "[(0, 1)]\n", - "[(0, 3)]\n", - "[(0, 2), (0, 3), (0, 4), (0, 5), (2, 3), (2, 4), (2, 5), (3, 4), (3, 5), (4, 5)]\n", - "[(0, 5), (0, 6), (5, 6)]\n", - "[(0, 5), (0, 7), (0, 8), (0, 9), (5, 7), (5, 8), (5, 9), (7, 8), (7, 9), (8, 9)]\n", - "[(9, 10)]\n", - "[(10, 11)]\n", - "[(10, 11), (10, 12), (11, 12)]\n", - "1804\n", - "[(0, 10)]\n", - "[(0, 9), (0, 11), (9, 11)]\n", - "[(0, 9), (0, 10), (0, 12), (9, 10), (9, 12), (10, 12)]\n", - "[(2, 3)]\n", - "[(2, 3), (2, 4), (2, 5), (2, 7), (2, 8), (2, 9), (3, 4), (3, 5), (3, 7), (3, 8), (3, 9), (4, 5), (4, 7), (4, 8), (4, 9), (5, 7), (5, 8), (5, 9), (7, 8), (7, 9), (8, 9)]\n", - "[(5, 6)]\n", - "[(5, 7)]\n", - "[(9, 10)]\n", - "0 1801\n" - ] - }, - { - "ename": "TypeError", - "evalue": "'module' object is not callable", - "output_type": "error", - "traceback": [ - "\u001B[0;31m---------------------------------------------------------------------------\u001B[0m", - "\u001B[0;31mTypeError\u001B[0m Traceback (most recent call last)", - "\u001B[0;32m<ipython-input-1-b2ac26fc56e7>\u001B[0m in \u001B[0;36m<module>\u001B[0;34m\u001B[0m\n\u001B[1;32m 4\u001B[0m \u001B[0;32mimport\u001B[0m \u001B[0mjson_read\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 5\u001B[0m \u001B[0;32mimport\u001B[0m \u001B[0mprojection\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m----> 6\u001B[0;31m \u001B[0;32mimport\u001B[0m \u001B[0mlouvain\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m 7\u001B[0m \u001B[0;32mimport\u001B[0m \u001B[0mutils\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 8\u001B[0m \u001B[0;32mimport\u001B[0m \u001B[0mtemporal_trade_off\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n", - "\u001B[0;32m~/Projects/dynCom/louvain.py\u001B[0m in \u001B[0;36m<module>\u001B[0;34m\u001B[0m\n\u001B[1;32m 35\u001B[0m \u001B[0mlouv\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0mLouvain\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 36\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m---> 37\u001B[0;31m \u001B[0mp\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0mlouv\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mapply\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mgraph_dyn_uni\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m 38\u001B[0m \u001B[0;31m#\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 39\u001B[0m \u001B[0;31m# #%%\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n", - "\u001B[0;32m~/Projects/dynCom/temporal_trade_off.py\u001B[0m in \u001B[0;36mapply\u001B[0;34m(self, dyn_graph)\u001B[0m\n\u001B[1;32m 32\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 33\u001B[0m \u001B[0;32mif\u001B[0m \u001B[0mi\u001B[0m \u001B[0;34m==\u001B[0m \u001B[0;36m0\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m---> 34\u001B[0;31m \u001B[0mfirst_partition\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0mself\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0msnapshot_clustering\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0msnapshot\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0;32mNone\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m 35\u001B[0m \u001B[0;32mcontinue\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 36\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n", - "\u001B[0;32m~/Projects/dynCom/louvain.py\u001B[0m in \u001B[0;36msnapshot_clustering\u001B[0;34m(self, snapshot, init_partition)\u001B[0m\n\u001B[1;32m 20\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 21\u001B[0m \u001B[0;32mdef\u001B[0m \u001B[0msnapshot_clustering\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mself\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0msnapshot\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0minit_partition\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m---> 22\u001B[0;31m \u001B[0mpartition\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0mcommunity_louvain\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0msnapshot\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mpartition\u001B[0m\u001B[0;34m=\u001B[0m\u001B[0minit_partition\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m 23\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 24\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n", - "\u001B[0;31mTypeError\u001B[0m: 'module' object is not callable" - ] - } - ], + "execution_count": 3, + "outputs": [], "source": [ "import rapidjson\n", "import networkx as nx\n", @@ -76,7 +23,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 4, "metadata": { "collapsed": true }, @@ -92,48 +39,65 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 5, + "outputs": [ + { + "data": { + "text/plain": "<dynetx.classes.dyngraph.DynGraph at 0x7fd2fbe9cb80>" + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "graph_dyn_uni = projection.projection(graph_dyn)\n", + "graph_dyn_uni\n" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 14, + "outputs": [ + { + "data": { + "text/plain": "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]" + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "graph_dyn_uni.time_slice(1803).nodes()" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 6, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "1801\n", - "[(0, 1)]\n", - "[(0, 2)]\n", - "[(5, 6)]\n", - "[(5, 7)]\n", - "1802\n", - "[(0, 1)]\n", - "[(0, 2)]\n", - "[(0, 3)]\n", - "[(0, 4)]\n", - "[(4, 5)]\n", - "[(5, 6)]\n", - "[(5, 7)]\n", - "1803\n", - "[(0, 1)]\n", - "[(0, 3)]\n", - "[(0, 2), (0, 3), (0, 4), (0, 5), (2, 3), (2, 4), (2, 5), (3, 4), (3, 5), (4, 5)]\n", - "[(0, 5), (0, 6), (5, 6)]\n", - "[(0, 5), (0, 7), (0, 8), (0, 9), (5, 7), (5, 8), (5, 9), (7, 8), (7, 9), (8, 9)]\n", - "[(9, 10)]\n", - "[(10, 11)]\n", - "[(10, 11), (10, 12), (11, 12)]\n", - "1804\n", - "[(0, 10)]\n", - "[(0, 9), (0, 11), (9, 11)]\n", - "[(0, 9), (0, 10), (0, 12), (9, 10), (9, 12), (10, 12)]\n", - "[(2, 3)]\n", - "[(2, 3), (2, 4), (2, 5), (2, 7), (2, 8), (2, 9), (3, 4), (3, 5), (3, 7), (3, 8), (3, 9), (4, 5), (4, 7), (4, 8), (4, 9), (5, 7), (5, 8), (5, 9), (7, 8), (7, 9), (8, 9)]\n", - "[(5, 6)]\n", - "[(5, 7)]\n", - "[(9, 10)]\n" + "[0, 1, 2, 5, 6, 7, 3, 4, 8, 9, 10, 11, 12]\n" ] } ], "source": [ - "graph_dyn_uni = projection.projection(graph_dyn)" + "print(graph_dyn_uni.nodes())" ], "metadata": { "collapsed": false, @@ -144,7 +108,7 @@ }, { "cell_type": "code", - "execution_count": 85, + "execution_count": 40, "outputs": [], "source": [ "louv = louvain.Louvain()" @@ -158,19 +122,16 @@ }, { "cell_type": "code", - "execution_count": 86, + "execution_count": 41, "outputs": [ { - "ename": "TypeError", - "evalue": "add_nodes_from() missing 1 required positional argument: 'nodes_for_adding'", - "output_type": "error", - "traceback": [ - "\u001B[0;31m---------------------------------------------------------------------------\u001B[0m", - "\u001B[0;31mTypeError\u001B[0m Traceback (most recent call last)", - "\u001B[0;32m<ipython-input-86-cbe6fc7cfdad>\u001B[0m in \u001B[0;36m<module>\u001B[0;34m\u001B[0m\n\u001B[0;32m----> 1\u001B[0;31m \u001B[0mp\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0mlouv\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mapply\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mgraph_dyn_uni\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m 2\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n", - "\u001B[0;32m~/Projects/dynCom/temporal_trade_off.py\u001B[0m in \u001B[0;36mapply\u001B[0;34m(self, dyn_graph)\u001B[0m\n\u001B[1;32m 16\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 17\u001B[0m \u001B[0;32mfor\u001B[0m \u001B[0mi\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0msnapshot_id\u001B[0m \u001B[0;32min\u001B[0m \u001B[0menumerate\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0msnapshot_ids\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m---> 18\u001B[0;31m \u001B[0msnapshot\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0mnx\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mGraph\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m 19\u001B[0m \u001B[0msnapshot\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0madd_nodes_from\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mdyn_graph\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mtime_slice\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0msnapshot_id\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mnodes\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 20\u001B[0m \u001B[0;31m# snapshot.add_edges_from(dyn_graph.time_slice(snapshot_id).edges())\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n", - "\u001B[0;32m~/Projects/dynCom/utils.py\u001B[0m in \u001B[0;36mdyn_to_nxgraph\u001B[0;34m(dyn_graph)\u001B[0m\n\u001B[1;32m 4\u001B[0m \u001B[0;32mdef\u001B[0m \u001B[0mdyn_to_nxgraph\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mdyn_graph\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 5\u001B[0m \u001B[0mgraph\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0mnx\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mGraph\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m----> 6\u001B[0;31m \u001B[0mnodes\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0mdyn_graph\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mnodes\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m 7\u001B[0m \u001B[0mgraph\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0madd_nodes_from\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mnodes_for_adding\u001B[0m\u001B[0;34m=\u001B[0m\u001B[0mnodes\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 8\u001B[0m \u001B[0mgraph\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0madd_edges_from\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mdyn_graph\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0minteractions\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n", - "\u001B[0;31mTypeError\u001B[0m: add_nodes_from() missing 1 required positional argument: 'nodes_for_adding'" + "name": "stdout", + "output_type": "stream", + "text": [ + "0 1801\n", + "1 1802\n", + "2 1803\n", + "3 1804\n" ] } ], @@ -186,13 +147,36 @@ }, { "cell_type": "code", - "execution_count": 80, + "execution_count": 42, + "outputs": [ + { + "data": { + "text/plain": "{1801: {0: [0, 1, 2], 1: [5, 6, 7]},\n 1802: {0: [0, 1, 2, 3], 1: [4, 5, 6, 7]},\n 1803: {0: [0, 1, 2, 3, 4], 1: [5, 6, 7, 8, 9], 2: [10, 11, 12]},\n 1804: {0: [0, 9, 10, 11, 12], 1: [2, 3, 4, 5, 7, 8, 6]}}" + }, + "execution_count": 42, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "p.communities" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 6, "outputs": [ { "data": { "text/plain": "[1801, 1802, 1803, 1804]" }, - "execution_count": 80, + "execution_count": 6, "metadata": {}, "output_type": "execute_result" } @@ -209,13 +193,13 @@ }, { "cell_type": "code", - "execution_count": 67, + "execution_count": 7, "outputs": [ { "data": { "text/plain": "dynetx.classes.dyngraph.DynGraph" }, - "execution_count": 67, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" } @@ -233,13 +217,13 @@ }, { "cell_type": "code", - "execution_count": 68, + "execution_count": 8, "outputs": [ { "data": { "text/plain": "[0, 9, 10, 11, 12, 2, 3, 4, 5, 7, 8, 6]" }, - "execution_count": 68, + "execution_count": 8, "metadata": {}, "output_type": "execute_result" } @@ -256,19 +240,15 @@ }, { "cell_type": "code", - "execution_count": 60, + "execution_count": 9, "outputs": [ { - "ename": "TypeError", - "evalue": "add_nodes_from() missing 1 required positional argument: 'nodes_for_adding'", - "output_type": "error", - "traceback": [ - "\u001B[0;31m---------------------------------------------------------------------------\u001B[0m", - "\u001B[0;31mTypeError\u001B[0m Traceback (most recent call last)", - "\u001B[0;32m<ipython-input-60-c57278ae38ca>\u001B[0m in \u001B[0;36m<module>\u001B[0;34m\u001B[0m\n\u001B[0;32m----> 1\u001B[0;31m \u001B[0mutils\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mdyn_to_nxgraph\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0msnap\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m 2\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n", - "\u001B[0;32m~/Projects/dynCom/utils.py\u001B[0m in \u001B[0;36mdyn_to_nxgraph\u001B[0;34m(dyn_graph)\u001B[0m\n\u001B[1;32m 4\u001B[0m \u001B[0;32mdef\u001B[0m \u001B[0mdyn_to_nxgraph\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mdyn_graph\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 5\u001B[0m \u001B[0mgraph\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0mnx\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mGraph\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m----> 6\u001B[0;31m \u001B[0mnodes\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0mdyn_graph\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mnodes\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m 7\u001B[0m \u001B[0mgraph\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0madd_nodes_from\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mnodes_for_adding\u001B[0m\u001B[0;34m=\u001B[0m\u001B[0mnodes\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 8\u001B[0m \u001B[0mgraph\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0madd_edges_from\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mdyn_graph\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0minteractions\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n", - "\u001B[0;31mTypeError\u001B[0m: add_nodes_from() missing 1 required positional argument: 'nodes_for_adding'" - ] + "data": { + "text/plain": "<networkx.classes.graph.Graph at 0x7ffa4527ecd0>" + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ @@ -283,7 +263,7 @@ }, { "cell_type": "code", - "execution_count": 65, + "execution_count": 10, "outputs": [], "source": [ "graph = nx.Graph()" @@ -297,7 +277,7 @@ }, { "cell_type": "code", - "execution_count": 87, + "execution_count": 11, "outputs": [], "source": [ "graph.add_nodes_from(snap.nodes())" diff --git a/dynCom/notebooks/tiles.ipynb b/dynCom/notebooks/tiles.ipynb new file mode 100644 index 0000000..e7c31b8 --- /dev/null +++ b/dynCom/notebooks/tiles.ipynb @@ -0,0 +1,160 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "import rapidjson\n", + "import networkx as nx\n", + "\n", + "import json_read\n", + "import projection\n", + "import louvain\n", + "import utils\n", + "import temporal_trade_off\n", + "from cdlib import algorithms" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "outputs": [], + "source": [ + "fp = \"../data/Marie_Boucher_small.json\"\n", + "with open(fp, \"r\", encoding='utf-8') as read_file:\n", + " json_data = rapidjson.loads(read_file.read())\n", + "\n", + "graph_dyn = json_read.paoh_json_reader(json_data)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 21, + "outputs": [ + { + "data": { + "text/plain": "<dynetx.classes.dyngraph.DynGraph at 0x7f460f684400>" + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "graph_dyn_uni = projection.projection(graph_dyn)\n", + "graph_dyn_uni" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 19, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[('_e_0', 0, '+', 1801), ('_e_0', 1, '+', 1801), ('_e_3', 0, '+', 1801), ('_e_3', 2, '+', 1801), ('_e_17', 5, '+', 1801), ('_e_17', 6, '+', 1801), ('_e_20', 5, '+', 1801), ('_e_20', 7, '+', 1801), ('_e_1', 0, '+', 1802), ('_e_1', 1, '+', 1802), ('_e_4', 0, '+', 1802), ('_e_4', 2, '+', 1802), ('_e_6', 0, '+', 1802), ('_e_6', 3, '+', 1802), ('_e_7', 0, '+', 1802), ('_e_7', 4, '+', 1802), ('_e_16', 4, '+', 1802), ('_e_16', 5, '+', 1802), ('_e_19', 5, '+', 1802), ('_e_19', 6, '+', 1802), ('_e_22', 5, '+', 1802), ('_e_22', 7, '+', 1802), ('_e_2', 0, '+', 1803), ('_e_2', 1, '+', 1803), ('_e_5', 0, '+', 1803), ('_e_5', 3, '+', 1803), ('_e_8', 0, '+', 1803), ('_e_8', 2, '+', 1803), ('_e_8', 3, '+', 1803), ('_e_8', 4, '+', 1803), ('_e_8', 5, '+', 1803), ('_e_9', 0, '+', 1803), ('_e_9', 5, '+', 1803), ('_e_9', 6, '+', 1803), ('_e_10', 0, '+', 1803), ('_e_10', 5, '+', 1803), ('_e_10', 7, '+', 1803), ('_e_10', 8, '+', 1803), ('_e_10', 9, '+', 1803), ('_e_24', 9, '+', 1803), ('_e_24', 10, '+', 1803), ('_e_25', 10, '+', 1803), ('_e_25', 11, '+', 1803), ('_e_26', 10, '+', 1803), ('_e_26', 11, '+', 1803), ('_e_26', 12, '+', 1803), ('_e_11', 0, '+', 1804), ('_e_11', 10, '+', 1804), ('_e_12', 0, '+', 1804), ('_e_12', 11, '+', 1804), ('_e_12', 9, '+', 1804), ('_e_13', 0, '+', 1804), ('_e_13', 9, '+', 1804), ('_e_13', 10, '+', 1804), ('_e_13', 12, '+', 1804), ('_e_14', 2, '+', 1804), ('_e_14', 3, '+', 1804), ('_e_15', 2, '+', 1804), ('_e_15', 3, '+', 1804), ('_e_15', 4, '+', 1804), ('_e_15', 5, '+', 1804), ('_e_15', 7, '+', 1804), ('_e_15', 8, '+', 1804), ('_e_15', 9, '+', 1804), ('_e_18', 5, '+', 1804), ('_e_18', 6, '+', 1804), ('_e_21', 5, '+', 1804), ('_e_21', 7, '+', 1804), ('_e_23', 9, '+', 1804), ('_e_23', 10, '+', 1804)]\n", + "[1801, 1802, 1803, 1804]\n" + ] + } + ], + "source": [ + "print(list(graph_dyn.stream_interactions()))\n", + "print(graph_dyn.temporal_snapshots_ids())" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 51, + "outputs": [], + "source": [ + "coms = algorithms.tiles(graph_dyn_uni, 1)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 52, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[1, 2, 3, 4, 5]\n", + "[]\n" + ] + } + ], + "source": [ + "print(coms.get_observation_ids())\n", + "print(coms.get_clustering_at(1).communities)\n" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/dynCom/setup.py b/dynCom/setup.py index 20a5af5..335d482 100644 --- a/dynCom/setup.py +++ b/dynCom/setup.py @@ -17,7 +17,7 @@ def read(fname): setup( name="dynCom", - version="0.0.1", + version="0.0.2", author="Alexis Pister", author_email="alexis.pister@inria.fr", description="Dynamic Graph Clustering Library", @@ -35,11 +35,12 @@ setup( # installed or upgraded on the target machine # install_requires=required, install_requires=[ - "networkx>=2.3", - "dynetx==0.2.3" + "networkx>=2.5", + "dynetx>=0.2.3", + "cdlib>=0.2.3" ], package_data={ # If any package contains *.md, *.txt or *.rst files, include them: 'doc': ['*.md', '*.rst'], - } - ) + } +) diff --git a/dynCom/tests/conftest.py b/dynCom/tests/conftest.py index 47058f5..f063194 100644 --- a/dynCom/tests/conftest.py +++ b/dynCom/tests/conftest.py @@ -20,7 +20,7 @@ def graph_dyn_bipartite(): @pytest.fixture(scope="session", params=[marie_boucher_small_fp, "generate"]) -def graph_dyn(request): +def graphs_dyn(request): if request.param == "generate": G = dn.DynGraph(edge_removal=True) G.add_interactions_from([(1, 2), (2, 3), (10, 11)], t=10) @@ -33,3 +33,11 @@ def graph_dyn(request): G = projection.projection(G_bipartite) return G + +@pytest.fixture(scope="session") +def marie_boucher_graph(): + json_data = rapidjson.loads(open(marie_boucher_small_fp, "r").read()) + G_bipartite = json_read.paoh_json_reader(json_data) + G = projection.projection(G_bipartite) + return G + diff --git a/dynCom/tests/test_InstantOptimal.py b/dynCom/tests/test_InstantOptimal.py new file mode 100644 index 0000000..12ea478 --- /dev/null +++ b/dynCom/tests/test_InstantOptimal.py @@ -0,0 +1,10 @@ +from dynCom import InstantOptimal + +def test_apply(marie_boucher_graph): + model = InstantOptimal.InstantOptimal("louvain") + dyn_communities = model.apply(marie_boucher_graph) + print(dyn_communities.to_json()) + + model = InstantOptimal.InstantOptimal("infomap") + dyn_communities = model.apply(marie_boucher_graph) + print(dyn_communities.to_json()) diff --git a/dynCom/tests/test_temporal_trade_off.py b/dynCom/tests/test_temporal_trade_off.py index 28a83a9..f562594 100644 --- a/dynCom/tests/test_temporal_trade_off.py +++ b/dynCom/tests/test_temporal_trade_off.py @@ -5,18 +5,20 @@ class TestGlobalOptUpdate: def test_snapshot_clustering(self): assert True - def test_apply(self, graph_dyn): + def test_apply(self, graphs_dyn): louvain_model = louvain.Louvain() - dyn_communities = louvain_model.apply(graph_dyn) + dyn_communities = louvain_model.apply(graphs_dyn) - assert len(dyn_communities.communities) == len(graph_dyn.temporal_snapshots_ids()) + assert len(dyn_communities.communities) == len(graphs_dyn.temporal_snapshots_ids()) - for snapshot_id in graph_dyn.temporal_snapshots_ids(): + for snapshot_id in graphs_dyn.temporal_snapshots_ids(): print(dyn_communities.communities[snapshot_id]) - snapshot = graph_dyn.time_slice(snapshot_id) + snapshot = graphs_dyn.time_slice(snapshot_id) assert len(snapshot) == len([node for com_id, com in dyn_communities.communities[snapshot_id].items() for node in com]) + print(dyn_communities.to_json()) + def test_save_to_dyn_communities(self): assert True diff --git a/dynCom/tests/test_utils.py b/dynCom/tests/test_utils.py index 31444ed..2ba49d8 100644 --- a/dynCom/tests/test_utils.py +++ b/dynCom/tests/test_utils.py @@ -3,10 +3,10 @@ import networkx as nx from dynCom.utils import dyn_to_nxgraph -def test_dyn_to_nxgraph(graph_dyn): - graph = dyn_to_nxgraph(graph_dyn) +def test_dyn_to_nxgraph(graphs_dyn): + graph = dyn_to_nxgraph(graphs_dyn) assert type(graph) == nx.Graph - assert set(graph_dyn.nodes()) == set(graph.nodes()) - assert {(u,v) for u, v, t in graph_dyn.interactions()} == set(graph.edges()) + assert set(graphs_dyn.nodes()) == set(graph.nodes()) + assert {(u,v) for u, v, t in graphs_dyn.interactions()} == set(graph.edges()) diff --git a/setup.py b/setup.py index 5970858..885c998 100644 --- a/setup.py +++ b/setup.py @@ -40,7 +40,7 @@ setup( # install_requires=required, install_requires=[ "python-louvain==0.13", - "cdlib==0.1.8", + "cdlib>=0.1.8", "docutils==0.15.2", "pquality==0.0.7", "bimlpa==0.1.2", @@ -66,7 +66,7 @@ setup( "pulp==1.6.10", "python-rapidjson==0.9.1", "pytiled-parser==0.9.3", - "networkx==2.3", + "networkx>=2.3", "requests>=2.22.0", "scikit-learn", "pandas", -- GitLab