Skip to content
This repository has been archived by the owner on Jan 30, 2023. It is now read-only.

Commit

Permalink
trac #27424: move dominating_set to domination.py
Browse files Browse the repository at this point in the history
  • Loading branch information
dcoudert committed Oct 20, 2019
1 parent c937e57 commit 4454cc0
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 113 deletions.
116 changes: 116 additions & 0 deletions src/sage/graphs/domination.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
:widths: 30, 70
:delim: |
:meth:`~dominating_set` | Return a minimum dominating set of the graph.
:meth:`~minimal_dominating_sets` | Return an iterator over the minimal dominating sets of a graph.
:meth:`~is_dominating` | Check whether ``dom`` is a dominating set of ``G``.
:meth:`~is_redundant` | Check whether a ``dom`` has redundant vertices.
Expand Down Expand Up @@ -59,6 +60,7 @@
# ****************************************************************************

from copy import copy
from sage.rings.integer import Integer


def is_dominating(G, dom, focus=None):
Expand Down Expand Up @@ -205,6 +207,120 @@ def private_neighbors(G, vertex, dom):
if neighbor not in closed_neighborhood_vs)


# ==============================================================================
# Computation of minimum dominating
# ==============================================================================

def dominating_set(g, independent=False, total=False, value_only=False, solver=None, verbose=0):
r"""
Return a minimum dominating set of the graph.
A minimum dominating set `S` of a graph `G` is a set of its vertices of
minimal cardinality such that any vertex of `G` is in `S` or has one of its
neighbors in `S`. See the :wikipedia:`Dominating_set`.
As an optimization problem, it can be expressed as:
.. MATH::
\mbox{Minimize : }&\sum_{v\in G} b_v\\
\mbox{Such that : }&\forall v \in G, b_v+\sum_{(u,v)\in G.edges()} b_u\geq 1\\
&\forall x\in G, b_x\mbox{ is a binary variable}
INPUT:
- ``independent`` -- boolean (default: ``False``); when ``True``, computes a
minimum independent dominating set, that is a minimum dominating set that
is also an independent set (see also
:meth:`~sage.graphs.graph.independent_set`)
- ``total`` -- boolean (default: ``False``); when ``True``, computes a total
dominating set (see the See the :wikipedia:`Dominating_set`)
- ``value_only`` -- boolean (default: ``False``); whether to only return the
cardinality of the computed dominating set, or to return its list of
vertices (default)
- ``solver`` -- (default: ``None``); specifies a Linear Program (LP) solver
to be used. If set to ``None``, the default one is used. For more
information on LP solvers and which default solver is used, see the method
:meth:`solve <sage.numerical.mip.MixedIntegerLinearProgram.solve>` of the
class :class:`MixedIntegerLinearProgram
<sage.numerical.mip.MixedIntegerLinearProgram>`.
- ``verbose`` -- integer (default: ``0``); sets the level of verbosity. Set
to 0 by default, which means quiet.
EXAMPLES:
A basic illustration on a ``PappusGraph``::
sage: g = graphs.PappusGraph()
sage: g.dominating_set(value_only=True)
5
If we build a graph from two disjoint stars, then link their centers we will
find a difference between the cardinality of an independent set and a stable
independent set::
sage: g = 2 * graphs.StarGraph(5)
sage: g.add_edge(0, 6)
sage: len(g.dominating_set())
2
sage: len(g.dominating_set(independent=True))
6
The total dominating set of the Petersen graph has cardinality 4::
sage: G = graphs.PetersenGraph()
sage: G.dominating_set(total=True, value_only=True)
4
The dominating set is calculated for both the directed and undirected graphs
(modification introduced in :trac:`17905`)::
sage: g = digraphs.Path(3)
sage: g.dominating_set(value_only=True)
2
sage: g = graphs.PathGraph(3)
sage: g.dominating_set(value_only=True)
1
"""
g._scream_if_not_simple(allow_multiple_edges=True, allow_loops=not total)

from sage.numerical.mip import MixedIntegerLinearProgram
p = MixedIntegerLinearProgram(maximization=False, solver=solver)
b = p.new_variable(binary=True)

# For any vertex v, one of its neighbors or v itself is in the minimum
# dominating set. If g is directed, we use the in neighbors of v instead.

neighbors_iter = g.neighbor_in_iterator if g.is_directed() else g.neighbor_iterator

if total:
# We want a total dominating set
for v in g:
p.add_constraint(p.sum(b[u] for u in neighbors_iter(v)), min=1)
else:
for v in g:
p.add_constraint(b[v] + p.sum(b[u] for u in neighbors_iter(v)), min=1)

if independent:
# no two adjacent vertices are in the set
for u, v in g.edge_iterator(labels=None):
p.add_constraint(b[u] + b[v], max=1)

# Minimizes the number of vertices used
p.set_objective(p.sum(b[v] for v in g))

if value_only:
return Integer(round(p.solve(objective_only=True, log=verbose)))
else:
p.solve(log=verbose)
b = p.get_values(b)
return [v for v in g if b[v] == 1]


# ==============================================================================
# Enumeration of minimal dominating set as described in [BDHPR2019]_
Expand Down
114 changes: 1 addition & 113 deletions src/sage/graphs/generic_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -9543,119 +9543,6 @@ def vertex_disjoint_paths(self, s, t, solver=None, verbose=0):

return paths

def dominating_set(self, independent=False, total=False, value_only=False, solver=None, verbose=0):
r"""
Return a minimum dominating set of the graph.

A minimum dominating set `S` of a graph `G` is a set of its vertices of
minimal cardinality such that any vertex of `G` is in `S` or has one of
its neighbors in `S`. See the :wikipedia:`Dominating_set`.

As an optimization problem, it can be expressed as:

.. MATH::

\mbox{Minimize : }&\sum_{v\in G} b_v\\
\mbox{Such that : }&\forall v \in G, b_v+\sum_{(u,v)\in G.edges()} b_u\geq 1\\
&\forall x\in G, b_x\mbox{ is a binary variable}

INPUT:

- ``independent`` -- boolean (default: ``False``); when ``True``,
computes a minimum independent dominating set, that is a minimum
dominating set that is also an independent set (see also
:meth:`~sage.graphs.graph.independent_set`)

- ``total`` -- boolean (default: ``False``); when ``True``, computes a
total dominating set (see the See the :wikipedia:`Dominating_set`)

- ``value_only`` -- boolean (default: ``False``); whether to only return
the cardinality of the computed dominating set, or to return its list
of vertices (default)

- ``solver`` -- (default: ``None``); specifies a Linear Program (LP)
solver to be used. If set to ``None``, the default one is used. For
more information on LP solvers and which default solver is used, see
the method :meth:`solve
<sage.numerical.mip.MixedIntegerLinearProgram.solve>` of the class
:class:`MixedIntegerLinearProgram
<sage.numerical.mip.MixedIntegerLinearProgram>`.

- ``verbose`` -- integer (default: ``0``); sets the level of
verbosity. Set to 0 by default, which means quiet.

EXAMPLES:

A basic illustration on a ``PappusGraph``::

sage: g = graphs.PappusGraph()
sage: g.dominating_set(value_only=True)
5

If we build a graph from two disjoint stars, then link their centers we
will find a difference between the cardinality of an independent set and
a stable independent set::

sage: g = 2 * graphs.StarGraph(5)
sage: g.add_edge(0, 6)
sage: len(g.dominating_set())
2
sage: len(g.dominating_set(independent=True))
6

The total dominating set of the Petersen graph has cardinality 4::

sage: G = graphs.PetersenGraph()
sage: G.dominating_set(total=True, value_only=True)
4

The dominating set is calculated for both the directed and undirected
graphs (modification introduced in :trac:`17905`)::

sage: g = digraphs.Path(3)
sage: g.dominating_set(value_only=True)
2
sage: g = graphs.PathGraph(3)
sage: g.dominating_set(value_only=True)
1

"""
self._scream_if_not_simple(allow_multiple_edges=True, allow_loops=not total)

from sage.numerical.mip import MixedIntegerLinearProgram
g = self
p = MixedIntegerLinearProgram(maximization=False, solver=solver)
b = p.new_variable(binary=True)

# For any vertex v, one of its neighbors or v itself is in
# the minimum dominating set. If g is directed, we use the
# in neighbors of v instead.

neighbors_iter = g.neighbor_in_iterator if g.is_directed() else g.neighbor_iterator

if total:
# We want a total dominating set
for v in g:
p.add_constraint(p.sum(b[u] for u in neighbors_iter(v)), min=1)
else:
for v in g:
p.add_constraint(b[v] + p.sum(b[u] for u in neighbors_iter(v)), min=1)

if independent:
# no two adjacent vertices are in the set
for u,v in g.edge_iterator(labels=None):
p.add_constraint(b[u] + b[v], max=1)

# Minimizes the number of vertices used
p.set_objective(p.sum(b[v] for v in g))

if value_only:
return Integer(round(p.solve(objective_only=True, log=verbose)))
else:
p.solve(log=verbose)
b = p.get_values(b)
return [v for v in g if b[v] == 1]

def pagerank(self, alpha=0.85, personalization=None, by_weight=False,
weight_function=None, dangling=None, algorithm=None):
r"""
Expand Down Expand Up @@ -23378,6 +23265,7 @@ def is_self_complementary(self):
from sage.graphs.connectivity import is_cut_vertex
from sage.graphs.connectivity import edge_connectivity
from sage.graphs.connectivity import vertex_connectivity
from sage.graphs.domination import dominating_set
from sage.graphs.base.static_dense_graph import connected_subgraph_iterator
from sage.graphs.path_enumeration import shortest_simple_paths
from sage.graphs.path_enumeration import all_paths
Expand Down

0 comments on commit 4454cc0

Please sign in to comment.