Skip to content

Commit

Permalink
Merge pull request #10 from azimov/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
azimov committed Oct 9, 2018
2 parents 37ce0e6 + d1121bc commit 64ff0bf
Show file tree
Hide file tree
Showing 8 changed files with 110 additions and 39 deletions.
16 changes: 16 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
language: python
python:
- "2.7"
- "3.5"
- "3.6"
- "3.7-dev"
# command to install dependencies
install:
- sudo apt install -y libboost-dev
- pip install -r requirements.txt codecov
- pip install .
# command to run tests
script:
- py.test --cov=cigram
after_success:
- codecov
21 changes: 18 additions & 3 deletions README.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
CiGRAM - Circular Gaussian Random gRAph Model
#############################################

|Build Status| |Coverage Status| |PyPI| |docs|

CiGRAM is a method for generating large complex networks with heterogeneous properties observed in real-world networks such as heavy tailed node degree distributions, assortative connectivity and community structure.
Currently the model only supports unweighted, undirected graphs.

Expand All @@ -15,7 +17,7 @@ Gilbert, J. P. "A probabilistic model for the evaluation of module extraction al

Bibtex entry:

.. code-block:: guess
.. code-block::
@phdthesis{gilbert2015probabilistic,
title={A probabilistic model for the evaluation of module extraction algorithms in complex biological networks},
Expand All @@ -39,13 +41,13 @@ does not work.

Clone repository and use pip (virtualenv is suggested):

.. code-block:: guess
.. code-block:: bash
pip install cigram
Alternatively, the package developed here can be installed by cloning the git repository and running the command:

.. code-block:: guess
.. code-block:: bash
python setup.py install
Expand All @@ -62,3 +64,16 @@ To generate a random model with 1000 nodes, average degree of 5 split in to 5 co
graph, vertex_positions, community_memberships = cigram.cigram_graph(1000, 5, 5)
graph is a networkx model, vertex_positions is a dict of vertex positions by node id, and community memberships is a dict of vertex memberships of each cluster.


.. |Build Status| image:: https://travis-ci.org/azimov/cigram.svg?branch=master
:target: https://travis-ci.org/azimov/cigram
.. |Coverage Status| image:: https://codecov.io/github/azimov/cigram/coverage.svg?branch=master
:target: https://codecov.io/github/azimov/cigram
.. |Build status2| image:: https://ci.appveyor.com/api/projects/status/
:target: https://ci.appveyor.com/project/azimov/cigram/branch/master
.. |PyPI| image:: https://badge.fury.io/py/cigram.svg
:target: https://pypi.python.org/pypi/cigram
.. |docs| image:: https://readthedocs.org/projects/cigram/badge/?style=flat
:target: https://readthedocs.org/projects/cigram
:alt: Documentation Status
2 changes: 1 addition & 1 deletion cigram/generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,4 +214,4 @@ def lfr_benchmark_graph(n, average_degree, max_degree, mu, tau=2.0, tau2=1.0, mi

graph.name = "lfr_binary_" + "-".join([str(p) for p in params])

return graph, memberships
return graph, memberships
76 changes: 43 additions & 33 deletions cigram/network_opt/cigram_optimiser.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,13 @@ def __init__(self, g, fit_evaluator,
"""

self.graph = g

avg_degree = sum(d for n, d in g.degree()) / float(g.number_of_nodes())
self.fixed_model_params = {
"n": g.number_of_nodes(),
"density": nx.density(g),
"connected": False,
"min_degree": min_degree
"n": g.number_of_nodes(),
"avg_deg": avg_degree,
"connected": False,
"min_degree": min_degree
}

self.gbest = None
Expand All @@ -71,19 +73,19 @@ def __init__(self, g, fit_evaluator,
self.starting_population = starting_population

if max_n is not None and max_n < g.number_of_nodes():
scaling_factor = g.number_of_nodes()/max_n
self.fixed_model_params["n"] = max_n
self.fixed_model_params["density"] *= scaling_factor
scaling_factor = g.number_of_nodes()/max_n
self.fixed_model_params["n"] = max_n
self.fixed_model_params["density"] *= scaling_factor

if k_bounds is None:
k_bounds = (1, int(self.fixed_model_params["N"]/20))
k_bounds = (1, int(self.fixed_model_params["n"]/20))

# Fix other parameters if we're not generating community strucuture
if k_bounds == 1:
csf_bounds = 0
csr_bounds = 0
ek_per_bounds = 0
po_bounds = 0
csf_bounds = 0
csr_bounds = 0
ek_per_bounds = 0
po_bounds = 0

# Set the boundaries of the optimisation that map to the parameters.
self.param_boundaries = OrderedDict()
Expand All @@ -98,27 +100,29 @@ def __init__(self, g, fit_evaluator,

self.random_param_func = dict([(p, random.uniform) for p in self.param_boundaries.keys()])
self.random_param_func["k"] = random.randint

for p, bounds in self.param_boundaries.items():
if type(bounds) is not tuple:
self.fixed_model_params[p] = bounds
del self.param_boundaries[p]
elif bounds[0] == bounds[1]:
self.fixed_model_params[p] = bounds[0]
del self.param_boundaries[p]

param_boundaries_cpy = self.param_boundaries.copy()

for p, bounds in param_boundaries_cpy.items():
if type(bounds) is not tuple:
self.fixed_model_params[p] = bounds
del self.param_boundaries[p]
elif bounds[0] == bounds[1]:
self.fixed_model_params[p] = bounds[0]
del self.param_boundaries[p]

if "name" in kwargs:
self.name = kwargs["name"]
self.name = kwargs["name"]
else:
self.name = "{0}".format(g.name)
self.name = "{0}".format(g.name)

self.fit_evaluator = fit_evaluator
# Store the target degree distribution/assortativity/clustering
# can be already specified if user wants to fit e.g. a degree distribution not obtained from a graph
if graph_props is not None:
self.graph_props_a = graph_props
self.graph_props_a = graph_props
else:
self.graph_props_a = self.fit_evaluator.graph_properties(g)
self.graph_props_a = self.fit_evaluator.graph_properties(g)

self.seed = seed

Expand All @@ -129,7 +133,7 @@ def set_gbest(self, pbest):
:return: None
"""
if self.gbest is None or self.gbest[1] > pbest[1]:
self.gbest = pbest
self.gbest = pbest

def set_name(self, name):
"""
Expand All @@ -143,18 +147,18 @@ def generate(self, candidate):
Generate the model given a candidates parameter set.
Must return a graph object
"""
params = dict(self.fixed_model_params.items() + self.convert_params(candidate).items())
params = self.get_params(candidate)

if self.seed is not None:
params["seed"] = self.seed
self.seed += 1
params["seed"] = self.seed
self.seed += 1

logger.debug("Generating {0}".format(params))
g, _, _ = cigram_graph(**params)
logger.debug("Generated {0}".format(params))
return g

def parameter_generator(self):
def parameter_generator(self, random=None, args=None):
"""
Generates a random set of parameters for use by the optimiser
if the starting population kwarg is used at init, the population is not randomised but starts with specified
Expand All @@ -173,7 +177,7 @@ def convert_params(self, candidate):
A candidate set will be in a given order (specified by the parameter_generator method)
This function just converts the params to those used by the model generator function.
"""
keys = self.param_boundaries.keys()
keys = list(self.param_boundaries.keys())
return dict((keys[i], candidate[i]) for i in range(len(candidate)))

def get_candidate(self, parameters):
Expand All @@ -183,8 +187,14 @@ def get_candidate(self, parameters):
return [parameters[p] for p in self.param_boundaries]

def get_params(self, candidate):

return dict(self.fixed_model_params.items() + self.convert_params(candidate).items())
rd = {}
for k, v in self.fixed_model_params.items():
rd[k] = v

for k, v in self.convert_params(candidate).items():
rd[k] = v

return rd

def get_fitness(self, candidate):
"""
Expand All @@ -207,7 +217,7 @@ def get_fitness_avg(self, candidate, num_reps=1):
"""

if num_reps < 1:
num_reps = 1
num_reps = 1
graphs = [self.generate(candidate) for _ in range(num_reps)]
graph_props_b = self.fit_evaluator.graph_properties_avg(graphs)
return self.fit_evaluator.graph_fitness(self.graph_props_a, graph_props_b)
Expand Down
14 changes: 14 additions & 0 deletions cigram/network_opt/fitness_measures.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import networkx as nx
import numpy as np
from networkx.algorithms.approximation.clustering_coefficient import average_clustering
import netlsd


def ks_distance(g_degree, m_degree, d_min=1, normed=True):
Expand Down Expand Up @@ -43,6 +44,19 @@ def ks_distance(g_degree, m_degree, d_min=1, normed=True):
return np.max(dists)


class NetLSDFit(object):
"""
Uses NetLSD spectral method for graph distance
"""
@staticmethod
def graph_properties(g):
return netlsd.heat(g)

@staticmethod
def graph_fitness(heat_a, heat_b):
return netlsd.compare(heat_a, heat_b)


class SummaryStatFitness(object):

def __init__(self,
Expand Down
2 changes: 1 addition & 1 deletion cigram/network_opt/model_parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ def cma_es_optimise_model(model,
"""

# Start optimiser with random starting parameters
es = cma.CMAEvolutionStrategy(model.parameter_generator(np.random, {}), cma_sigma, dict(verb_disp=0, verbose=0))
es = cma.CMAEvolutionStrategy(model.parameter_generator(), cma_sigma, dict(verb_disp=0, verbose=0))

def fitness_wrap(x, res, tlock):
fit = model.get_fitness(x)
Expand Down
4 changes: 3 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ pytest>=3.1.2
scipy>=0.19.1
inspyred>=1.0.1
click>=6.6
cma>=1.1.7
cma>=1.1.7
netlsd>=1.0.0
pytest-cov>=2.5.1
14 changes: 14 additions & 0 deletions tests/test_fitting.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import networkx as nx
from cigram.network_opt.cigram_optimiser import CigramOptimiser
from cigram.network_opt.model_parameters import optimise_model
from cigram.network_opt.fitness_measures import NetLSDFit


def test_fitter():
"""
General test of code, should just run without error.
:return:
"""
graph = nx.karate_club_graph()
mdl = CigramOptimiser(graph, NetLSDFit, k_bounds=2)
results = optimise_model(mdl, max_evals=200)

0 comments on commit 64ff0bf

Please sign in to comment.