Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[contrib] Refactor incidence_analysis to cache a graph instead of a matrix #2715

Merged
merged 29 commits into from
Feb 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
85e6a0f
run black on incidence_analysis
Robbybp Jan 31, 2023
1b8a2b5
comments on planned implementation using a graph instead of a matrix
Robbybp Jan 31, 2023
78cf760
update to cache an incidence graph instead of incidence matrix
Robbybp Jan 31, 2023
2e9f92f
update maximum_matching to accept a bipartite graph as well as matrix
Robbybp Jan 31, 2023
be47836
add get_bipartite_incidence_graph to __init__ and start tests
Robbybp Jan 31, 2023
a7ab473
add tests and docstring for extract_bipartite_subgraph
Robbybp Feb 1, 2023
8a62a96
get_adjacent_to method and tests
Robbybp Feb 1, 2023
f9c9f04
update methods to use graph instead of matrix
Robbybp Feb 1, 2023
73033eb
update maximum_matching to accept a bipartite graph with fewer assump…
Robbybp Feb 1, 2023
4793345
allow block_triangularize to accept a bipartite graph
Robbybp Feb 1, 2023
e6d9c82
update block_triangularize to use the cached graph instead of a matri…
Robbybp Feb 1, 2023
847d5b8
refactor block_triangularize to use an intermediate get_scc_of_projec…
Robbybp Feb 1, 2023
bf1d8a2
update get_scc_of_projection to return a list of lists of tuples
Robbybp Feb 1, 2023
1bdea8f
update docstring
Robbybp Feb 1, 2023
584bcbb
tests for get_scc_of_projection
Robbybp Feb 1, 2023
6d1106a
re-add self.cached=NUMERIC, even though it doesnt do anything anymore
Robbybp Feb 1, 2023
cd829b5
remove extra self.cached=NUMERIC; turns out I never removed it
Robbybp Feb 1, 2023
f3feb5d
use len(self.constraints) as this is more clear than len(con_index_map)
Robbybp Feb 1, 2023
84f43de
test to exercise _extract_subgraph when a matrix is cached
Robbybp Feb 1, 2023
1ed1638
Merge branch 'main' into incidence-graph
Robbybp Feb 1, 2023
f968efc
fix typos and remove extraneous include_fixed arguments
Robbybp Feb 2, 2023
0d0d08b
remove include_fixed=False in _extract_submatrix
Robbybp Feb 3, 2023
361b1eb
update get_diagonal_blocks to use get_scc_of_projection and remove un…
Robbybp Feb 3, 2023
62b78b4
comment on unused row_block_map attributes
Robbybp Feb 3, 2023
3192e67
Merge branch 'main' of https://github.com/pyomo/pyomo into incidence-…
Robbybp Feb 3, 2023
0448c32
remove unused top_nodes argument from block_triangularize
Robbybp Feb 3, 2023
5661e84
update docstring for correctness, then change location of edge revers…
Robbybp Feb 3, 2023
b067ffa
Merge branch 'main' into incidence-graph
Robbybp Feb 7, 2023
6f1995f
Merge branch 'main' into incidence-graph
mrmundt Feb 10, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions pyomo/contrib/incidence_analysis/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
from .triangularize import block_triangularize
from .matching import maximum_matching
from .interface import IncidenceGraphInterface
from .interface import (
IncidenceGraphInterface,
get_bipartite_incidence_graph,
)
from .util import (
generate_strongly_connected_components,
solve_strongly_connected_components,
)
generate_strongly_connected_components,
solve_strongly_connected_components,
)
31 changes: 16 additions & 15 deletions pyomo/contrib/incidence_analysis/common/dulmage_mendelsohn.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@


def _get_projected_digraph(bg, matching, top_nodes):
"""
"""
""" """
digraph = DiGraph()
digraph.add_nodes_from(top_nodes)
for n in top_nodes:
Expand All @@ -45,8 +44,7 @@ def _get_projected_digraph(bg, matching, top_nodes):


def _get_reachable_from(digraph, sources):
"""
"""
""" """
_filter = set()
reachable = []
for node in sources:
Expand Down Expand Up @@ -96,14 +94,17 @@ def dulmage_mendelsohn(bg, top_nodes=None, matching=None):
t_other = [t for t in top_nodes if t not in _filter]
b_other = [b for b in bot_nodes if b not in _filter]

return ((
t_unmatched,
t_reachable,
t_matched_with_reachable,
t_other,
), (
b_unmatched,
b_reachable,
b_matched_with_reachable,
b_other,
))
return (
(
t_unmatched,
t_reachable,
t_matched_with_reachable,
t_other,
),
(
b_unmatched,
b_reachable,
b_matched_with_reachable,
b_other,
),
)
8 changes: 3 additions & 5 deletions pyomo/contrib/incidence_analysis/connected.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@


def get_independent_submatrices(matrix):
"""
"""
""" """
nxc = nx.algorithms.components
nxb = nx.algorithms.bipartite
from_biadjacency_matrix = nxb.matrix.from_biadjacency_matrix
Expand All @@ -27,11 +26,10 @@ def get_independent_submatrices(matrix):
# nodes have values in [N, N+M-1].
# We could also check the "bipartite" attribute of each node...
row_blocks = [
sorted([node for node in comp if node < N])
for comp in connected_components
sorted([node for node in comp if node < N]) for comp in connected_components
]
col_blocks = [
sorted([node-N for node in comp if node >= N])
sorted([node - N for node in comp if node >= N])
for comp in connected_components
]
return row_blocks, col_blocks
40 changes: 21 additions & 19 deletions pyomo/contrib/incidence_analysis/dulmage_mendelsohn.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,22 @@
from pyomo.common.dependencies import networkx as nx
from pyomo.contrib.incidence_analysis.common.dulmage_mendelsohn import (
dulmage_mendelsohn as dm_nx,
)
)

"""
This module imports the general Dulmage-Mendelsohn-on-a-graph function
from "common" and implements an interface for coo_matrix-like objects.
"""

RowPartition = namedtuple(
"RowPartition",
["unmatched", "overconstrained", "underconstrained", "square"],
)
"RowPartition",
["unmatched", "overconstrained", "underconstrained", "square"],
)
ColPartition = namedtuple(
"ColPartition",
["unmatched", "underconstrained", "overconstrained", "square"],
)
"ColPartition",
["unmatched", "underconstrained", "overconstrained", "square"],
)


def dulmage_mendelsohn(matrix_or_graph, top_nodes=None, matching=None):
"""
Expand All @@ -45,9 +47,9 @@ def dulmage_mendelsohn(matrix_or_graph, top_nodes=None, matching=None):
graph = matrix_or_graph
if top_nodes is None:
raise ValueError(
"top_nodes must be specified if a graph is provided,"
"\notherwise the result is ambiguous."
)
"top_nodes must be specified if a graph is provided,"
"\notherwise the result is ambiguous."
)
partition = dm_nx(graph, top_nodes=top_nodes, matching=matching)
# RowPartition and ColPartition do not make sense for a general graph.
# However, here we assume that this graph comes from a Pyomo model,
Expand All @@ -74,17 +76,17 @@ def dulmage_mendelsohn(matrix_or_graph, top_nodes=None, matching=None):
# Matrix rows have bipartite=0, columns have bipartite=1
bg = from_biadjacency_matrix(matrix)
row_partition, col_partition = dm_nx(
bg,
top_nodes=list(range(M)),
matching=matching,
)
bg,
top_nodes=list(range(M)),
matching=matching,
)

partition = (
row_partition,
tuple([n - M for n in subset] for subset in col_partition)
# Column nodes have values in [M, M+N-1]. Apply the offset
# to get values corresponding to indices in user's matrix.
)
row_partition,
tuple([n - M for n in subset] for subset in col_partition)
# Column nodes have values in [M, M+N-1]. Apply the offset
# to get values corresponding to indices in user's matrix.
)

partition = (RowPartition(*partition[0]), ColPartition(*partition[1]))
return partition
Loading