323 lines
9.4 KiB
Python
323 lines
9.4 KiB
Python
|
import random
|
||
|
from copy import copy
|
||
|
|
||
|
import pytest
|
||
|
|
||
|
import networkx as nx
|
||
|
from networkx.utils import (
|
||
|
PythonRandomInterface,
|
||
|
arbitrary_element,
|
||
|
create_py_random_state,
|
||
|
create_random_state,
|
||
|
dict_to_numpy_array,
|
||
|
discrete_sequence,
|
||
|
flatten,
|
||
|
groups,
|
||
|
is_string_like,
|
||
|
iterable,
|
||
|
make_list_of_ints,
|
||
|
make_str,
|
||
|
pairwise,
|
||
|
powerlaw_sequence,
|
||
|
to_tuple,
|
||
|
)
|
||
|
from networkx.utils.misc import _dict_to_numpy_array1, _dict_to_numpy_array2
|
||
|
|
||
|
nested_depth = (
|
||
|
1,
|
||
|
2,
|
||
|
(3, 4, ((5, 6, (7,), (8, (9, 10), 11), (12, 13, (14, 15)), 16), 17), 18, 19),
|
||
|
20,
|
||
|
)
|
||
|
|
||
|
nested_set = {
|
||
|
(1, 2, 3, 4),
|
||
|
(5, 6, 7, 8, 9),
|
||
|
(10, 11, (12, 13, 14), (15, 16, 17, 18)),
|
||
|
19,
|
||
|
20,
|
||
|
}
|
||
|
|
||
|
nested_mixed = [
|
||
|
1,
|
||
|
(2, 3, {4, (5, 6), 7}, [8, 9]),
|
||
|
{10: "foo", 11: "bar", (12, 13): "baz"},
|
||
|
{(14, 15): "qwe", 16: "asd"},
|
||
|
(17, (18, "19"), 20),
|
||
|
]
|
||
|
|
||
|
|
||
|
@pytest.mark.parametrize("result", [None, [], ["existing"], ["existing1", "existing2"]])
|
||
|
@pytest.mark.parametrize("nested", [nested_depth, nested_mixed, nested_set])
|
||
|
def test_flatten(nested, result):
|
||
|
if result is None:
|
||
|
val = flatten(nested, result)
|
||
|
assert len(val) == 20
|
||
|
else:
|
||
|
_result = copy(result) # because pytest passes parameters as is
|
||
|
nexisting = len(_result)
|
||
|
val = flatten(nested, _result)
|
||
|
assert len(val) == len(_result) == 20 + nexisting
|
||
|
|
||
|
assert issubclass(type(val), tuple)
|
||
|
|
||
|
|
||
|
def test_is_string_like():
|
||
|
assert is_string_like("aaaa")
|
||
|
assert not is_string_like(None)
|
||
|
assert not is_string_like(123)
|
||
|
|
||
|
|
||
|
def test_iterable():
|
||
|
assert not iterable(None)
|
||
|
assert not iterable(10)
|
||
|
assert iterable([1, 2, 3])
|
||
|
assert iterable((1, 2, 3))
|
||
|
assert iterable({1: "A", 2: "X"})
|
||
|
assert iterable("ABC")
|
||
|
|
||
|
|
||
|
def test_graph_iterable():
|
||
|
K = nx.complete_graph(10)
|
||
|
assert iterable(K)
|
||
|
assert iterable(K.nodes())
|
||
|
assert iterable(K.edges())
|
||
|
|
||
|
|
||
|
def test_make_list_of_ints():
|
||
|
mylist = [1, 2, 3.0, 42, -2]
|
||
|
assert make_list_of_ints(mylist) is mylist
|
||
|
assert make_list_of_ints(mylist) == mylist
|
||
|
assert type(make_list_of_ints(mylist)[2]) is int
|
||
|
pytest.raises(nx.NetworkXError, make_list_of_ints, [1, 2, 3, "kermit"])
|
||
|
pytest.raises(nx.NetworkXError, make_list_of_ints, [1, 2, 3.1])
|
||
|
|
||
|
|
||
|
def test_random_number_distribution():
|
||
|
# smoke test only
|
||
|
z = powerlaw_sequence(20, exponent=2.5)
|
||
|
z = discrete_sequence(20, distribution=[0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 3])
|
||
|
|
||
|
|
||
|
def test_make_str_with_bytes():
|
||
|
x = "qualité"
|
||
|
y = make_str(x)
|
||
|
assert isinstance(y, str)
|
||
|
assert len(y) == 7
|
||
|
|
||
|
|
||
|
def test_make_str_with_unicode():
|
||
|
x = "qualité"
|
||
|
y = make_str(x)
|
||
|
assert isinstance(y, str)
|
||
|
assert len(y) == 7
|
||
|
|
||
|
|
||
|
class TestNumpyArray:
|
||
|
@classmethod
|
||
|
def setup_class(cls):
|
||
|
global np
|
||
|
np = pytest.importorskip("numpy")
|
||
|
|
||
|
def test_numpy_to_list_of_ints(self):
|
||
|
a = np.array([1, 2, 3], dtype=np.int64)
|
||
|
b = np.array([1.0, 2, 3])
|
||
|
c = np.array([1.1, 2, 3])
|
||
|
assert type(make_list_of_ints(a)) == list
|
||
|
assert make_list_of_ints(b) == list(b)
|
||
|
B = make_list_of_ints(b)
|
||
|
assert type(B[0]) == int
|
||
|
pytest.raises(nx.NetworkXError, make_list_of_ints, c)
|
||
|
|
||
|
def test__dict_to_numpy_array1(self):
|
||
|
d = {"a": 1, "b": 2}
|
||
|
a = _dict_to_numpy_array1(d, mapping={"a": 0, "b": 1})
|
||
|
np.testing.assert_allclose(a, np.array([1, 2]))
|
||
|
a = _dict_to_numpy_array1(d, mapping={"b": 0, "a": 1})
|
||
|
np.testing.assert_allclose(a, np.array([2, 1]))
|
||
|
|
||
|
a = _dict_to_numpy_array1(d)
|
||
|
np.testing.assert_allclose(a.sum(), 3)
|
||
|
|
||
|
def test__dict_to_numpy_array2(self):
|
||
|
d = {"a": {"a": 1, "b": 2}, "b": {"a": 10, "b": 20}}
|
||
|
|
||
|
mapping = {"a": 1, "b": 0}
|
||
|
a = _dict_to_numpy_array2(d, mapping=mapping)
|
||
|
np.testing.assert_allclose(a, np.array([[20, 10], [2, 1]]))
|
||
|
|
||
|
a = _dict_to_numpy_array2(d)
|
||
|
np.testing.assert_allclose(a.sum(), 33)
|
||
|
|
||
|
def test_dict_to_numpy_array_a(self):
|
||
|
d = {"a": {"a": 1, "b": 2}, "b": {"a": 10, "b": 20}}
|
||
|
|
||
|
mapping = {"a": 0, "b": 1}
|
||
|
a = dict_to_numpy_array(d, mapping=mapping)
|
||
|
np.testing.assert_allclose(a, np.array([[1, 2], [10, 20]]))
|
||
|
|
||
|
mapping = {"a": 1, "b": 0}
|
||
|
a = dict_to_numpy_array(d, mapping=mapping)
|
||
|
np.testing.assert_allclose(a, np.array([[20, 10], [2, 1]]))
|
||
|
|
||
|
a = _dict_to_numpy_array2(d)
|
||
|
np.testing.assert_allclose(a.sum(), 33)
|
||
|
|
||
|
def test_dict_to_numpy_array_b(self):
|
||
|
d = {"a": 1, "b": 2}
|
||
|
|
||
|
mapping = {"a": 0, "b": 1}
|
||
|
a = dict_to_numpy_array(d, mapping=mapping)
|
||
|
np.testing.assert_allclose(a, np.array([1, 2]))
|
||
|
|
||
|
a = _dict_to_numpy_array1(d)
|
||
|
np.testing.assert_allclose(a.sum(), 3)
|
||
|
|
||
|
|
||
|
def test_pairwise():
|
||
|
nodes = range(4)
|
||
|
node_pairs = [(0, 1), (1, 2), (2, 3)]
|
||
|
node_pairs_cycle = node_pairs + [(3, 0)]
|
||
|
assert list(pairwise(nodes)) == node_pairs
|
||
|
assert list(pairwise(iter(nodes))) == node_pairs
|
||
|
assert list(pairwise(nodes, cyclic=True)) == node_pairs_cycle
|
||
|
empty_iter = iter(())
|
||
|
assert list(pairwise(empty_iter)) == []
|
||
|
empty_iter = iter(())
|
||
|
assert list(pairwise(empty_iter, cyclic=True)) == []
|
||
|
|
||
|
|
||
|
def test_groups():
|
||
|
many_to_one = dict(zip("abcde", [0, 0, 1, 1, 2]))
|
||
|
actual = groups(many_to_one)
|
||
|
expected = {0: {"a", "b"}, 1: {"c", "d"}, 2: {"e"}}
|
||
|
assert actual == expected
|
||
|
assert {} == groups({})
|
||
|
|
||
|
|
||
|
def test_to_tuple():
|
||
|
a_list = [1, 2, [1, 3]]
|
||
|
actual = to_tuple(a_list)
|
||
|
expected = (1, 2, (1, 3))
|
||
|
assert actual == expected
|
||
|
|
||
|
a_tuple = (1, 2)
|
||
|
actual = to_tuple(a_tuple)
|
||
|
expected = a_tuple
|
||
|
assert actual == expected
|
||
|
|
||
|
a_mix = (1, 2, [1, 3])
|
||
|
actual = to_tuple(a_mix)
|
||
|
expected = (1, 2, (1, 3))
|
||
|
assert actual == expected
|
||
|
|
||
|
|
||
|
def test_create_random_state():
|
||
|
np = pytest.importorskip("numpy")
|
||
|
rs = np.random.RandomState
|
||
|
|
||
|
assert isinstance(create_random_state(1), rs)
|
||
|
assert isinstance(create_random_state(None), rs)
|
||
|
assert isinstance(create_random_state(np.random), rs)
|
||
|
assert isinstance(create_random_state(rs(1)), rs)
|
||
|
# Support for numpy.random.Generator
|
||
|
rng = np.random.default_rng()
|
||
|
assert isinstance(create_random_state(rng), np.random.Generator)
|
||
|
pytest.raises(ValueError, create_random_state, "a")
|
||
|
|
||
|
assert np.all(rs(1).rand(10) == create_random_state(1).rand(10))
|
||
|
|
||
|
|
||
|
def test_create_py_random_state():
|
||
|
pyrs = random.Random
|
||
|
|
||
|
assert isinstance(create_py_random_state(1), pyrs)
|
||
|
assert isinstance(create_py_random_state(None), pyrs)
|
||
|
assert isinstance(create_py_random_state(pyrs(1)), pyrs)
|
||
|
pytest.raises(ValueError, create_py_random_state, "a")
|
||
|
|
||
|
np = pytest.importorskip("numpy")
|
||
|
|
||
|
rs = np.random.RandomState
|
||
|
rng = np.random.default_rng(1000)
|
||
|
rng_explicit = np.random.Generator(np.random.SFC64())
|
||
|
nprs = PythonRandomInterface
|
||
|
assert isinstance(create_py_random_state(np.random), nprs)
|
||
|
assert isinstance(create_py_random_state(rs(1)), nprs)
|
||
|
assert isinstance(create_py_random_state(rng), nprs)
|
||
|
assert isinstance(create_py_random_state(rng_explicit), nprs)
|
||
|
# test default rng input
|
||
|
assert isinstance(PythonRandomInterface(), nprs)
|
||
|
|
||
|
|
||
|
def test_PythonRandomInterface_RandomState():
|
||
|
np = pytest.importorskip("numpy")
|
||
|
|
||
|
rs = np.random.RandomState
|
||
|
rng = PythonRandomInterface(rs(42))
|
||
|
rs42 = rs(42)
|
||
|
|
||
|
# make sure these functions are same as expected outcome
|
||
|
assert rng.randrange(3, 5) == rs42.randint(3, 5)
|
||
|
assert rng.choice([1, 2, 3]) == rs42.choice([1, 2, 3])
|
||
|
assert rng.gauss(0, 1) == rs42.normal(0, 1)
|
||
|
assert rng.expovariate(1.5) == rs42.exponential(1 / 1.5)
|
||
|
assert np.all(rng.shuffle([1, 2, 3]) == rs42.shuffle([1, 2, 3]))
|
||
|
assert np.all(
|
||
|
rng.sample([1, 2, 3], 2) == rs42.choice([1, 2, 3], (2,), replace=False)
|
||
|
)
|
||
|
assert np.all(
|
||
|
[rng.randint(3, 5) for _ in range(100)]
|
||
|
== [rs42.randint(3, 6) for _ in range(100)]
|
||
|
)
|
||
|
assert rng.random() == rs42.random_sample()
|
||
|
|
||
|
|
||
|
def test_PythonRandomInterface_Generator():
|
||
|
np = pytest.importorskip("numpy")
|
||
|
|
||
|
rng = np.random.default_rng(42)
|
||
|
pri = PythonRandomInterface(np.random.default_rng(42))
|
||
|
|
||
|
# make sure these functions are same as expected outcome
|
||
|
assert pri.randrange(3, 5) == rng.integers(3, 5)
|
||
|
assert pri.choice([1, 2, 3]) == rng.choice([1, 2, 3])
|
||
|
assert pri.gauss(0, 1) == rng.normal(0, 1)
|
||
|
assert pri.expovariate(1.5) == rng.exponential(1 / 1.5)
|
||
|
assert np.all(pri.shuffle([1, 2, 3]) == rng.shuffle([1, 2, 3]))
|
||
|
assert np.all(
|
||
|
pri.sample([1, 2, 3], 2) == rng.choice([1, 2, 3], (2,), replace=False)
|
||
|
)
|
||
|
assert np.all(
|
||
|
[pri.randint(3, 5) for _ in range(100)]
|
||
|
== [rng.integers(3, 6) for _ in range(100)]
|
||
|
)
|
||
|
assert pri.random() == rng.random()
|
||
|
|
||
|
|
||
|
@pytest.mark.parametrize(
|
||
|
("iterable_type", "expected"), ((list, 1), (tuple, 1), (str, "["), (set, 1))
|
||
|
)
|
||
|
def test_arbitrary_element(iterable_type, expected):
|
||
|
iterable = iterable_type([1, 2, 3])
|
||
|
assert arbitrary_element(iterable) == expected
|
||
|
|
||
|
|
||
|
@pytest.mark.parametrize(
|
||
|
"iterator", ((i for i in range(3)), iter([1, 2, 3])) # generator
|
||
|
)
|
||
|
def test_arbitrary_element_raises(iterator):
|
||
|
"""Value error is raised when input is an iterator."""
|
||
|
with pytest.raises(ValueError, match="from an iterator"):
|
||
|
arbitrary_element(iterator)
|
||
|
|
||
|
|
||
|
def test_dict_to_numpy_array_deprecations():
|
||
|
np = pytest.importorskip("numpy")
|
||
|
d = {"a": 1}
|
||
|
with pytest.deprecated_call():
|
||
|
nx.utils.dict_to_numpy_array1(d)
|
||
|
d2 = {"a": {"b": 2}}
|
||
|
with pytest.deprecated_call():
|
||
|
nx.utils.dict_to_numpy_array2(d2)
|