Giter VIP home page Giter VIP logo

hypothesis-networkx's Introduction

Build Status codecov

Hypothesis-networkx

This module provides a Hypothesis strategy for generating networkx graphs. This can be used to efficiently and thoroughly test your code.

Installation

This module can be installed via pip:

pip install hypothesis-networkx

User guide

The module exposes a single function: graph_builder. This function is a hypothesis composite strategy for building graphs. You can use it as follows:

from hypothesis_networkx import graph_builder
from hypothesis import strategies as st
import networkx as nx

node_data = st.fixed_dictionaries({'name': st.text(),
                                   'number': st.integers()})
edge_data = st.fixed_dictionaries({'weight': st.floats(allow_nan=False,
                                                       allow_infinity=False)})


builder = graph_builder(graph_type=nx.Graph,
                        node_keys=st.integers(),
                        node_data=node_data,
                        edge_data=edge_data,
                        min_nodes=2, max_nodes=10,
                        min_edges=1, max_edges=None,
                        self_loops=False,
                        connected=True)

graph = builder.example()
print(graph.nodes(data=True))
print(graph.edges(data=True))

Of course this builder is a valid hypothesis strategy, and using it to just make examples is not super useful. Instead, you can (and should) use it in your testing framework:

from hypothesis import given

@given(graph=builder)
def test_my_function(graph):
    assert my_function(graph) == known_function(graph)

The meaning of the arguments given to graph_builder are pretty self-explanatory, but they must be given as keyword arguments.

  • node_data: The strategy from which node attributes will be drawn.
  • edge_data: The strategy from which edge attributes will be drawn.
  • node_keys: Either the strategy from which node keys will be draw, or None. If None, node keys will be integers from the range (0, number of nodes).
  • min_nodes and max_nodes: The minimum and maximum number of nodes the produced graphs will contain.
  • min_edges and max_edges: The minimum and maximum number of edges the produced graphs will contain. Note that less edges than min_edges may be added if there are not enough nodes, and more than max_edges if connected is True.
  • graph_type: This function (or class) will be called without arguments to create an empty initial graph.
  • connected: If True, the generated graph is guaranteed to be a single connected component.
  • self_loops: If False, there will be no self-loops in the generated graph. Self-loops are edges between a node and itself.

Known limitations

There are a few (minor) outstanding issues with this module:

  • Graph generation may be slow for large graphs.
  • The min_edges argument is not always respected when the produced graph is too small.
  • The max_edges argument is not always respected if connected is True.
  • It currently works for Python 2.7, but this is considered deprecated and may stop working without notice.

See also

Networkx

Hypothesis

hypothesis-networkx's People

Contributors

gmacon avatar pckroon avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

hypothesis-networkx's Issues

Deprecation warning about using empty data for sampled_from

____________________________________________________________________________________________ test_graph_builder ____________________________________________________________________________________________

    @settings(max_examples=250, suppress_health_check=[HealthCheck.too_slow])
>   @given(st.data())
    def test_graph_builder(data):
        """
        Make sure the number of nodes and edges of the generated graphs is correct,
        and make sure that graphs that are supposed to be connected are.

../hypothesis-networkx/tests/test_graph_builder.py:30: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/bootsteps-py3.7/lib/python3.7/site-packages/hypothesis/core.py:606: in evaluate_test_data
    result = self.execute(data)
../../../.cache/pypoetry/virtualenvs/bootsteps-py3.7/lib/python3.7/site-packages/hypothesis/core.py:571: in execute
    result = self.test_runner(data, run)
../../../.cache/pypoetry/virtualenvs/bootsteps-py3.7/lib/python3.7/site-packages/hypothesis/executors.py:56: in default_new_style_executor
    return function(data)
../../../.cache/pypoetry/virtualenvs/bootsteps-py3.7/lib/python3.7/site-packages/hypothesis/core.py:569: in run
    return test(*args, **kwargs)
../hypothesis-networkx/tests/test_graph_builder.py:30: in test_graph_builder
    @given(st.data())
../../../.cache/pypoetry/virtualenvs/bootsteps-py3.7/lib/python3.7/site-packages/hypothesis/core.py:530: in test
    result = self.test(*args, **kwargs)
../hypothesis-networkx/tests/test_graph_builder.py:69: in test_graph_builder
    graph = data.draw(strategy)
../../../.cache/pypoetry/virtualenvs/bootsteps-py3.7/lib/python3.7/site-packages/hypothesis/_strategies.py:2105: in draw
    result = self.conjecture_data.draw(strategy)
../../../.cache/pypoetry/virtualenvs/bootsteps-py3.7/lib/python3.7/site-packages/hypothesis/internal/conjecture/data.py:830: in draw
    return self.__draw(strategy, label=label)
../../../.cache/pypoetry/virtualenvs/bootsteps-py3.7/lib/python3.7/site-packages/hypothesis/internal/conjecture/data.py:845: in __draw
    return strategy.do_draw(self)
../../../.cache/pypoetry/virtualenvs/bootsteps-py3.7/lib/python3.7/site-packages/hypothesis/searchstrategy/lazy.py:156: in do_draw
    return data.draw(self.wrapped_strategy)
../../../.cache/pypoetry/virtualenvs/bootsteps-py3.7/lib/python3.7/site-packages/hypothesis/internal/conjecture/data.py:830: in draw
    return self.__draw(strategy, label=label)
../../../.cache/pypoetry/virtualenvs/bootsteps-py3.7/lib/python3.7/site-packages/hypothesis/internal/conjecture/data.py:839: in __draw
    return strategy.do_draw(self)
../../../.cache/pypoetry/virtualenvs/bootsteps-py3.7/lib/python3.7/site-packages/hypothesis/_strategies.py:1872: in do_draw
    return self.definition(data.draw, *self.args, **self.kwargs)
../hypothesis-networkx/hypothesis_networkx/strategy.py:168: in graph_builder
    graph.add_edges_from((e[0][0], e[0][1], e[1]) for e in draw(edges))
../../../.cache/pypoetry/virtualenvs/bootsteps-py3.7/lib/python3.7/site-packages/hypothesis/internal/conjecture/data.py:824: in draw
    if strategy.is_empty:
../../../.cache/pypoetry/virtualenvs/bootsteps-py3.7/lib/python3.7/site-packages/hypothesis/searchstrategy/strategies.py:154: in accept
    recur(self)
../../../.cache/pypoetry/virtualenvs/bootsteps-py3.7/lib/python3.7/site-packages/hypothesis/searchstrategy/strategies.py:150: in recur
    mapping[strat] = getattr(strat, calculation)(recur)
../../../.cache/pypoetry/virtualenvs/bootsteps-py3.7/lib/python3.7/site-packages/hypothesis/searchstrategy/lazy.py:92: in calc_is_empty
    return recur(self.wrapped_strategy)
../../../.cache/pypoetry/virtualenvs/bootsteps-py3.7/lib/python3.7/site-packages/hypothesis/searchstrategy/lazy.py:107: in wrapped_strategy
    unwrapped_args = tuple(unwrap_strategies(s) for s in self.__args)
../../../.cache/pypoetry/virtualenvs/bootsteps-py3.7/lib/python3.7/site-packages/hypothesis/searchstrategy/lazy.py:107: in <genexpr>
    unwrapped_args = tuple(unwrap_strategies(s) for s in self.__args)
../../../.cache/pypoetry/virtualenvs/bootsteps-py3.7/lib/python3.7/site-packages/hypothesis/searchstrategy/lazy.py:51: in unwrap_strategies
    result = unwrap_strategies(s.wrapped_strategy)
../../../.cache/pypoetry/virtualenvs/bootsteps-py3.7/lib/python3.7/site-packages/hypothesis/searchstrategy/lazy.py:107: in wrapped_strategy
    unwrapped_args = tuple(unwrap_strategies(s) for s in self.__args)
../../../.cache/pypoetry/virtualenvs/bootsteps-py3.7/lib/python3.7/site-packages/hypothesis/searchstrategy/lazy.py:107: in <genexpr>
    unwrapped_args = tuple(unwrap_strategies(s) for s in self.__args)
../../../.cache/pypoetry/virtualenvs/bootsteps-py3.7/lib/python3.7/site-packages/hypothesis/searchstrategy/lazy.py:51: in unwrap_strategies
    result = unwrap_strategies(s.wrapped_strategy)
../../../.cache/pypoetry/virtualenvs/bootsteps-py3.7/lib/python3.7/site-packages/hypothesis/searchstrategy/lazy.py:112: in wrapped_strategy
    base = self.function(*self.__args, **self.__kwargs)
../../../.cache/pypoetry/virtualenvs/bootsteps-py3.7/lib/python3.7/site-packages/hypothesis/_strategies.py:700: in sampled_from
    since="2019-03-12",
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

message = 'sampled_from() with nothing to sample is deprecated and will be an error in a future version.  It currently returns `st.nothing()`, which if unexpected can make parts of a strategy silently vanish.'
since = '2019-03-12', verbosity = Verbosity.normal

    def note_deprecation(message, since, verbosity=None):
        # type: (str, str, Verbosity) -> None
        if verbosity is None:
            verbosity = settings.default.verbosity
        assert verbosity is not None
        if since != "RELEASEDAY":
            date = datetime.datetime.strptime(since, "%Y-%m-%d").date()
            assert datetime.date(2016, 1, 1) <= date
        warning = HypothesisDeprecationWarning(message)
        if verbosity > Verbosity.quiet:
>           warnings.warn(warning, stacklevel=2)
E           hypothesis.errors.HypothesisDeprecationWarning: sampled_from() with nothing to sample is deprecated and will be an error in a future version.  It currently returns `st.nothing()`, which if unexpected can make parts of a strategy silently vanish.

../../../.cache/pypoetry/virtualenvs/bootsteps-py3.7/lib/python3.7/site-packages/hypothesis/_settings.py:801: HypothesisDeprecationWarning

Generate only unique graphs

When running in debug mode I often get the following:

Trying example: test_execution_order_with_dependend_step(steps_dependency_graph=
Nodes: [0, 1]
Edges: [(0, 1)]

I'd rather tests only unique graphs if possible.

Thoughts on a new strategy

Hi @pckroon . I used your graph_builder strategy to create a new hypothesis strategy that I found to be quite useful. It is designed to generate a graph that has a guaranteed min/max number of connected component, and each component is guaranteed to have a min/max number of nodes.

I'm curious if you would be interested in adding this as a strategy to hypothesis-networkx. I also have tests for this that provides full coverage.

For more details, here is the full signature / docstring. As noted below, it is possible to provide parameters that over-constrain the graphs to be generated, in all such cases the strategy will raise a ValueError telling the user that their specification is unsatisfiable.

@composite
def graphs(
    draw,
    min_nodes: int,
    max_nodes: Optional[int] = None,
    min_num_components: Optional[int] = None,
    max_num_components: Optional[int] = None,
    min_component_size: int = 1,
    max_component_size: Optional[int] = None,
) -> st.SearchStrategy[nx.Graph]:
    """Draws graphs whose number of nodes, number of connected components, and
    size of connected components are constrained.

    Parameters
    ----------
    min_nodes : int
        The minimum number of nodes permitted in a graph.

    max_nodes : Optional[int]
        The largest number of nodes permitted in a graph. Defaults to
        ``10 + min_nodes``.

    min_num_components : Optional[int]
        The minimum number of connected components permitted in a graph.
        Defaults to the smallest permissible number based on ``max_component_size``,
        if such a constraint is specified, and the number of nodes that were drawn.
        Otherwise this defaults to 1.

    max_num_components : Optional[int]
        The maximum number of connected components permitted in a graph.
        Defaults to the largest permissible number based on ``min_component_size``
        and the number of nodes that were drawn.

    min_component_size : int
        The smallest permissible size for a connected component in the a graph. The
        default value is one node.

    max_component_size : Optional[int]
        The largest permissible size for a connected component in the a graph. The
        default value adapts according to the constraints of other parameters to permit
        the largest possible connected component.

    Returns
    -------
    st.SearchStrategy[nx.Graph]

    Notes
    -----
    The parameters provided here have the potential of over-constraining the graphs.
    It is recommended that you either constrain the number of connected components or
    their size, not both.

    Values drawn from this strategy shrink towards a graph with:
        - fewer nodes
        - fewer connected components
        - an even distribution of nodes among its connected components

    Also note that the partition-generation process scales harshly with number of nodes:
    it is inadvisable to draw graphs with more then tens of nodes.""

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.