Source code for hcga.features.dominating_sets
"""Dominating sets class."""
import networkx as nx
from hcga.feature_class import FeatureClass, InterpretabilityScore
featureclass_name = "DominatingSets"
[docs]def size_dominating_set(graph):
"""size_dominating_set"""
return len(list(nx.dominating_set(graph)))
[docs]def size_min_dominating_set(graph):
"""size_min_dominating_set"""
return len(list(min_weighted_dominating_set(graph)))
[docs]def size_min_edge_dominating_set(graph):
"""size_min_edge_dominating_set"""
return len(list(min_edge_dominating_set(graph)))
[docs]class DominatingSets(FeatureClass):
"""Dominating sets class.
Features based on dominating sets. Where *dominating set* for a graph
with node set *V* is a subset *D* of
*V* such that every node not in *D* is adjacent to at least one
member of *D* [1]_.
Uses networkx: `Networkx_dominating_set <https://networkx.github.io/documentation/\
stable/reference/algorithms/dominating.html>`_
References
----------
.. [1] https://en.wikipedia.org/wiki/Dominating_set
.. [2] Abdol-Hossein Esfahanian. Connectivity Algorithms.
http://www.cse.msu.edu/~cse835/Papers/Graph_connectivity_revised.pdf
"""
modes = ["fast", "medium", "slow"]
shortname = "DS"
name = "dominating_sets"
encoding = "networkx"
[docs] def compute_features(self):
self.add_feature(
"size_dominating_set",
size_dominating_set,
"The number of nodes in the dominating set",
InterpretabilityScore(3),
)
self.add_feature(
"size_min_dominating_set",
size_min_dominating_set,
"The number of nodes in the minimum weighted dominating set",
InterpretabilityScore(3),
)
self.add_feature(
"size_min_edge_dominating_set",
size_min_edge_dominating_set,
"The number of nodes in the minimum edge dominating set",
InterpretabilityScore(3),
)
[docs]def min_weighted_dominating_set(G, weight=None):
"""Taken from netoworkx."""
# The unique dominating set for the null graph is the empty set.
if len(G) == 0: # pylint: disable=len-as-condition
return set()
# This is the dominating set that will eventually be returned.
dom_set = set()
def _cost(node_and_neighborhood):
"""Returns the cost-effectiveness of greedily choosing the given node.
`node_and_neighborhood` is a two-tuple comprising a node and its
closed neighborhood.
"""
v, neighborhood = node_and_neighborhood
return G.nodes[v].get(weight, 1) / len(neighborhood - dom_set)
# This is a set of all vertices not already covered by the
# dominating set.
vertices = set(G)
# This is a dictionary mapping each node to the closed neighborhood
# of that node.
neighborhoods = {v: {v} | set(G[v]) for v in G}
# Continue until all vertices are adjacent to some node in the
# dominating set.
while vertices:
# Find the most cost-effective node to add, along with its
# closed neighborhood.
dom_node, min_set = min(neighborhoods.items(), key=_cost)
# Add the node to the dominating set and reduce the remaining
# set of nodes to cover.
dom_set.add(dom_node)
del neighborhoods[dom_node]
vertices -= min_set
return dom_set
[docs]def min_edge_dominating_set(G):
"""Taken from networkx."""
if not G:
raise ValueError("Expected non-empty NetworkX graph!")
return maximal_matching(G)
[docs]def maximal_matching(G):
"""Taken from networkx."""
matching = set()
nodes = set()
for u, v in G.edges():
# If the edge isn't covered, add it to the matching
# then remove neighborhood of u and v from consideration.
if u not in nodes and v not in nodes and u != v:
matching.add((u, v))
nodes.add(u)
nodes.add(v)
return matching