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

Added empty and complete graph generators #679

Merged
merged 6 commits into from
Oct 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions docs/source/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,10 @@ Generators
rustworkx.generators.generalized_petersen_graph
rustworkx.generators.barbell_graph
rustworkx.generators.full_rary_tree
rustworkx.generators.empty_graph
rustworkx.generators.directed_empty_graph
rustworkx.generators.complete_graph
rustworkx.generators.directed_complete_graph

.. _random_generators:

Expand Down
31 changes: 31 additions & 0 deletions releasenotes/notes/empty-and-complete-09d569b42cb6b9d5.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
features:
- |
Added new generator functions, :func:`~rustworkx.generators.empty_graph`,
and :func:`~rustworkx.generators.directed_empty_graph` to the
``rustworkx.generators`` module that will generate an empty graph.
For example:

.. jupyter-execute::

import rustworkx.generators
from rustworkx.visualization import mpl_draw

graph = rustworkx.generators.empty_graph(4)
mpl_draw(graph)

- |
Added new generator functions, :func:`~rustworkx.generators.complete_graph`,
and :func:`~rustworkx.generators.directed_complete_graph` to the
``rustworkx.generators`` module that will generate a complete graph.
These functions are equivalent to calling the :func:`~rustworkx.generators.mesh_graph`
and :func:`~rustworkx.generators.directed_mesh_graph` functions.
For example:

.. jupyter-execute::

import rustworkx.generators
from rustworkx.visualization import mpl_draw

graph = rustworkx.generators.complete_graph(4)
mpl_draw(graph)
152 changes: 152 additions & 0 deletions src/generators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2420,6 +2420,154 @@ pub fn barbell_graph(
})
}

/// Generate an undirected empty graph with ``n`` nodes and no edges.
///
/// :param int n: The number of nodes to generate the graph with.
///
/// :returns: The generated empty graph
/// :rtype: PyGraph
///
/// .. jupyter-execute::
///
/// import rustworkx.generators
/// from rustworkx.visualization import mpl_draw
///
/// graph = rustworkx.generators.empty_graph(5)
/// mpl_draw(graph)
///
#[pyfunction(multigraph = true)]
#[pyo3(text_signature = "(/, n, multigraph=True)")]
pub fn empty_graph(py: Python, n: usize, multigraph: bool) -> PyResult<graph::PyGraph> {
let mut graph = StableUnGraph::<PyObject, PyObject>::default();
for _ in 0..n {
graph.add_node(py.None());
}
Ok(graph::PyGraph {
graph,
node_removed: false,
multigraph,
attrs: py.None(),
})
}

/// Generate a directed empty graph with ``n`` nodes and no edges.
///
/// :param int n: The number of nodes to generate the graph with.
///
/// :returns: The generated empty graph
/// :rtype: PyDiGraph
///
/// .. jupyter-execute::
///
/// import rustworkx.generators
/// from rustworkx.visualization import mpl_draw
///
/// graph = rustworkx.generators.directed_empty_graph(5)
/// mpl_draw(graph)
///
#[pyfunction(multigraph = true)]
#[pyo3(text_signature = "(/, n, multigraph=True)")]
pub fn directed_empty_graph(
py: Python,
n: usize,
multigraph: bool,
) -> PyResult<digraph::PyDiGraph> {
let mut graph = StableDiGraph::<PyObject, PyObject>::default();
for _ in 0..n {
graph.add_node(py.None());
}
Ok(digraph::PyDiGraph {
graph,
node_removed: false,
cycle_state: algo::DfsSpace::default(),
check_cycle: false,
multigraph,
attrs: py.None(),
})
}

/// Generate an undirected complete graph with ``n`` nodes.
///
/// A complete graph is a simple graph in which each pair of distinct
/// vertices is connected by a unique edge.
/// The complete graph on ``n`` nodes is the graph with the set of nodes
/// ``{0, 1, ..., n-1}`` and the set of edges ``{(i, j) : i < j, 0 <= i < n, 0 <= j < n}``.
/// The number of edges in the complete graph is ``n*(n-1)/2``.
///
/// :param int num_node: The number of nodes to generate the graph with. Node
/// weights will be None if this is specified. If both ``num_node`` and
/// ``weights`` are set this will be ignored and ``weights`` will be used.
/// :param list weights: A list of node weights. If both ``num_node`` and
/// ``weights`` are set this will be ignored and ``weights`` will be used.
/// :param bool multigraph: When set to False the output
/// :class:`~rustworkx.PyGraph` object will not be not be a multigraph and
/// won't allow parallel edges to be added. Instead
/// calls which would create a parallel edge will update the existing edge.
///
/// :returns: The generated complete graph
/// :rtype: PyGraph
/// :raises IndexError: If neither ``num_nodes`` or ``weights`` are specified
///
/// .. jupyter-execute::
///
/// import rustworkx.generators
/// from rustworkx.visualization import mpl_draw
///
/// graph = rustworkx.generators.complete_graph(5)
/// mpl_draw(graph)
///
#[pyfunction(multigraph = true)]
#[pyo3(text_signature = "(/, num_nodes=None, weights=None, multigraph=True)")]
pub fn complete_graph(
py: Python,
num_nodes: Option<usize>,
weights: Option<Vec<PyObject>>,
multigraph: bool,
) -> PyResult<graph::PyGraph> {
mesh_graph(py, num_nodes, weights, multigraph)
}

/// Generate a directed complete graph with ``n`` nodes.
///
/// A directed complete graph is a directed graph in which each pair of distinct
/// vertices is connected by a unique pair of directed edges.
/// The directed complete graph on ``n`` nodes is the graph with the set of nodes
/// ``{0, 1, ..., n-1}`` and the set of edges ``{(i, j) : 0 <= i < n, 0 <= j < n}``.
/// The number of edges in the directed complete graph is ``n*(n-1)``.
///
/// :param int num_node: The number of nodes to generate the graph with. Node
/// weights will be None if this is specified. If both ``num_node`` and
/// ``weights`` are set this will be ignored and ``weights`` will be used.
/// :param list weights: A list of node weights. If both ``num_node`` and
/// ``weights`` are set this will be ignored and ``weights`` will be used.
/// :param bool multigraph: When set to False the output
/// :class:`~rustworkx.PyDiGraph` object will not be not be a multigraph and
/// won't allow parallel edges to be added. Instead
/// calls which would create a parallel edge will update the existing edge.
///
/// :returns: The generated directed complete graph
/// :rtype: PyDiGraph
/// :raises IndexError: If neither ``num_nodes`` or ``weights`` are specified
///
/// .. jupyter-execute::
///
/// import rustworkx.generators
/// from rustworkx.visualization import mpl_draw
///
/// graph = rustworkx.generators.directed_complete_graph(5)
/// mpl_draw(graph)
///
#[pyfunction(multigraph = true)]
#[pyo3(text_signature = "(/, num_nodes=None, weights=None, multigraph=True)")]
pub fn directed_complete_graph(
py: Python,
num_nodes: Option<usize>,
weights: Option<Vec<PyObject>>,
multigraph: bool,
) -> PyResult<digraph::PyDiGraph> {
directed_mesh_graph(py, num_nodes, weights, multigraph)
}

#[pymodule]
pub fn generators(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_wrapped(wrap_pyfunction!(cycle_graph))?;
Expand All @@ -2444,5 +2592,9 @@ pub fn generators(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_wrapped(wrap_pyfunction!(full_rary_tree))?;
m.add_wrapped(wrap_pyfunction!(generalized_petersen_graph))?;
m.add_wrapped(wrap_pyfunction!(barbell_graph))?;
m.add_wrapped(wrap_pyfunction!(empty_graph))?;
m.add_wrapped(wrap_pyfunction!(directed_empty_graph))?;
m.add_wrapped(wrap_pyfunction!(complete_graph))?;
m.add_wrapped(wrap_pyfunction!(directed_complete_graph))?;
Ok(())
}
30 changes: 30 additions & 0 deletions tests/rustworkx_tests/generators/test_complete.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

import unittest

import rustworkx


class TestCompleteGraph(unittest.TestCase):
def test_complete_graph(self):
for m in [0, 1, 3, 5]:
graph = rustworkx.generators.complete_graph(m)
self.assertEqual(len(graph), m)
self.assertEqual(len(graph.edges()), m * (m - 1) / 2)

def test_complete_directed_graph(self):
for m in [0, 1, 3, 5]:
graph = rustworkx.generators.directed_complete_graph(m)
self.assertEqual(len(graph), m)
self.assertEqual(len(graph.edges()), m * (m - 1))
self.assertIsInstance(graph, rustworkx.PyDiGraph)
28 changes: 28 additions & 0 deletions tests/rustworkx_tests/generators/test_empty.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

import unittest

import rustworkx


class TestEmptyGraph(unittest.TestCase):
def test_empty_graph(self):
graph = rustworkx.generators.empty_graph(20)
self.assertEqual(len(graph), 20)
self.assertEqual(len(graph.edges()), 0)

def test_empty_directed_graph(self):
graph = rustworkx.generators.directed_empty_graph(20)
self.assertEqual(len(graph), 20)
self.assertEqual(len(graph.edges()), 0)
self.assertIsInstance(graph, rustworkx.PyDiGraph)