Skip to content

Commit 507ef96

Browse files
authored
Merge pull request #1526 from rahuljoglekar47/add_community_detection
Add community detection
2 parents 41406e7 + 7f0128c commit 507ef96

File tree

12 files changed

+2264
-4
lines changed

12 files changed

+2264
-4
lines changed
250 KB
Loading
153 KB
Loading

doc/OnlineDocs/contributed_packages/community.rst

Lines changed: 368 additions & 0 deletions
Large diffs are not rendered by default.

doc/OnlineDocs/contributed_packages/index.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,9 @@ Contributed packages distributed with Pyomo:
2323
mindtpy.rst
2424
satsolver.rst
2525
sensitivity_toolbox.rst
26+
community.rst
2627

2728
Contributed packages distributed independently of Pyomo, but accessible
2829
through ``pyomo.contrib``:
2930

30-
* `pyomo.contrib.simplemodel <http://pyomocontrib-simplemodel.readthedocs.io/en/latest/source.html>`_
31+
* `pyomo.contrib.simplemodel <http://pyomocontrib-simplemodel.readthedocs.io/en/latest/source.html>`_

pyomo/contrib/community_detection/__init__.py

Whitespace-only changes.
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
"""Model Graph Generator Code"""
2+
3+
from pyomo.common.dependencies import networkx as nx
4+
from pyomo.core import Constraint, Objective, Var, ComponentMap, SortComponents
5+
from pyomo.core.expr.current import identify_variables
6+
from pyomo.contrib.community_detection.event_log import _event_log
7+
8+
9+
def generate_model_graph(model, type_of_graph, with_objective=True, weighted_graph=True,
10+
use_only_active_components=True):
11+
"""
12+
Creates a networkX graph of nodes and edges based on a Pyomo optimization model
13+
14+
This function takes in a Pyomo optimization model, then creates a graphical representation of the model with
15+
specific features of the graph determined by the user (see Parameters below).
16+
17+
(This function is designed to be called by detect_communities, but can be used solely for the purpose of
18+
creating model graphs as well.)
19+
20+
Parameters
21+
----------
22+
model: Block
23+
a Pyomo model or block to be used for community detection
24+
type_of_graph: str
25+
a string that specifies the type of graph that is created from the model
26+
'constraint' creates a graph based on constraint nodes,
27+
'variable' creates a graph based on variable nodes,
28+
'bipartite' creates a graph based on constraint and variable nodes (bipartite graph).
29+
with_objective: bool, optional
30+
a Boolean argument that specifies whether or not the objective function is included in the graph; the
31+
default is True
32+
weighted_graph: bool, optional
33+
a Boolean argument that specifies whether a weighted or unweighted graph is to be created from the Pyomo
34+
model; the default is True (type_of_graph='bipartite' creates an unweighted graph regardless of this parameter)
35+
use_only_active_components: bool, optional
36+
a Boolean argument that specifies whether inactive constraints/objectives are included in the networkX graph
37+
38+
Returns
39+
-------
40+
bipartite_model_graph/projected_model_graph: nx.Graph
41+
a NetworkX graph with nodes and edges based on the given Pyomo optimization model
42+
number_component_map: dict
43+
a dictionary that (deterministically) maps a number to a component in the model
44+
constraint_variable_map: dict
45+
a dictionary that maps a numbered constraint to a list of (numbered) variables that appear in the constraint
46+
"""
47+
48+
# Start off by making a bipartite graph (regardless of the value of type_of_graph), then if
49+
# type_of_graph = 'variable' or 'constraint', we will "collapse" this bipartite graph into a variable node
50+
# or constraint node graph
51+
52+
# Initialize the data structure needed to keep track of edges in the graph (this graph will be made
53+
# without edge weights, because edge weights are not useful for this bipartite graph)
54+
edge_set = set()
55+
56+
bipartite_model_graph = nx.Graph() # Initialize NetworkX graph for the bipartite graph
57+
constraint_variable_map = {} # Initialize map of the variables in constraint equations
58+
59+
# Make a dict of all the components we need for the NetworkX graph (since we cannot use the components directly
60+
# in the NetworkX graph)
61+
if with_objective:
62+
component_number_map = ComponentMap((component, number) for number, component in enumerate(
63+
model.component_data_objects(ctype=(Constraint, Var, Objective), active=use_only_active_components,
64+
descend_into=True,
65+
sort=SortComponents.deterministic)))
66+
else:
67+
component_number_map = ComponentMap((component, number) for number, component in enumerate(
68+
model.component_data_objects(ctype=(Constraint, Var), active=use_only_active_components, descend_into=True,
69+
sort=SortComponents.deterministic)))
70+
71+
# Create the reverse of component_number_map, which will be used in detect_communities to convert the node numbers
72+
# to their corresponding Pyomo modeling components
73+
number_component_map = dict((number, comp) for comp, number in component_number_map.items())
74+
75+
# Add the components as nodes to the bipartite graph
76+
bipartite_model_graph.add_nodes_from([node_number for node_number in range(len(component_number_map))])
77+
78+
# Loop through all constraints in the Pyomo model to determine what edges need to be created
79+
for model_constraint in model.component_data_objects(ctype=Constraint, active=use_only_active_components,
80+
descend_into=True):
81+
numbered_constraint = component_number_map[model_constraint]
82+
83+
# Create a list of the variable numbers that occur in the given constraint equation
84+
numbered_variables_in_constraint_equation = [component_number_map[constraint_variable]
85+
for constraint_variable in
86+
identify_variables(model_constraint.body)]
87+
88+
# Update constraint_variable_map
89+
constraint_variable_map[numbered_constraint] = numbered_variables_in_constraint_equation
90+
91+
# Create a list of all the edges that need to be created based on the variables in this constraint equation
92+
edges_between_nodes = [(numbered_constraint, numbered_variable_in_constraint)
93+
for numbered_variable_in_constraint in numbered_variables_in_constraint_equation]
94+
95+
# Update edge_set based on the determined edges between nodes
96+
edge_set.update(edges_between_nodes)
97+
98+
# This if statement will be executed if the user chooses to include the objective function as a node in
99+
# the model graph
100+
if with_objective:
101+
102+
# Use a loop to account for the possibility of multiple objective functions
103+
for objective_function in model.component_data_objects(ctype=Objective, active=use_only_active_components,
104+
descend_into=True):
105+
numbered_objective = component_number_map[objective_function]
106+
107+
# Create a list of the variable numbers that occur in the given objective function
108+
numbered_variables_in_objective = [component_number_map[objective_variable]
109+
for objective_variable in identify_variables(objective_function)]
110+
111+
# Update constraint_variable_map
112+
constraint_variable_map[numbered_objective] = numbered_variables_in_objective
113+
114+
# Create a list of all the edges that need to be created based on the variables in the objective function
115+
edges_between_nodes = [(numbered_objective, numbered_variable_in_objective)
116+
for numbered_variable_in_objective in numbered_variables_in_objective]
117+
118+
# Update edge_set based on the determined edges between nodes
119+
edge_set.update(edges_between_nodes)
120+
121+
# Add edges to bipartite_model_graph (the order in which edges are added can affect community detection, so
122+
# sorting prevents any unpredictable changes)
123+
bipartite_model_graph.add_edges_from(sorted(edge_set))
124+
125+
if type_of_graph == 'bipartite': # This is the case where the user wants a bipartite graph, which we made above
126+
# Log important information with the following logger function
127+
_event_log(model, bipartite_model_graph, set(constraint_variable_map), type_of_graph, with_objective)
128+
129+
# Return the bipartite NetworkX graph, the dictionary of node numbers mapped to their respective Pyomo
130+
# components, and the map of constraints to the variables they contain
131+
return bipartite_model_graph, number_component_map, constraint_variable_map
132+
133+
# At this point of the code, we will create the projected version of the bipartite
134+
# model graph (based on the specific value of type_of_graph)
135+
136+
constraint_nodes = set(constraint_variable_map)
137+
if type_of_graph == 'constraint':
138+
graph_nodes = constraint_nodes
139+
else:
140+
variable_nodes = set(number_component_map) - constraint_nodes
141+
graph_nodes = variable_nodes
142+
143+
if weighted_graph:
144+
projected_model_graph = nx.bipartite.weighted_projected_graph(bipartite_model_graph, graph_nodes)
145+
else:
146+
projected_model_graph = nx.bipartite.projected_graph(bipartite_model_graph, graph_nodes)
147+
148+
# Log important information with the following logger function
149+
_event_log(model, projected_model_graph, set(constraint_variable_map), type_of_graph, with_objective)
150+
151+
# Return the projected NetworkX graph, the dictionary of node numbers mapped to their respective Pyomo
152+
# components, and the map of constraints to the variables they contain
153+
return projected_model_graph, number_component_map, constraint_variable_map

0 commit comments

Comments
 (0)