NNGT/testing/test_basics.py

863 lines
23 KiB
Python
Executable File

# -*- coding: utf-8 -*-
# SPDX-FileCopyrightText: 2015-2023 Tanguy Fardet
# SPDX-License-Identifier: GPL-3.0-or-later
# testing/test_basics.py
"""
Test the validity of the most basic graph functions.
"""
import pytest
import numpy as np
import numpy.testing as npt
import nngt
import nngt.generation as ng
from nngt.lib import InvalidArgument
tolerance = 1e-6
def test_config():
'''
Check get/set_config functions.
'''
old_cfg = nngt.get_config(detailed=True)
# get config
cfg = nngt.get_config()
for k, v in cfg.items():
assert v == nngt.get_config(k)
cfg_detailed = nngt.get_config(detailed=True)
for k, v in cfg_detailed.items():
assert v == nngt.get_config(k)
# set config (omp)
num_omp = 2
nngt.set_config("omp", num_omp)
assert nngt.get_config("multithreading")
assert nngt.get_config("omp") == num_omp
# set config (mpi)
has_mpi = False
try:
import mpi4py
has_mpi = True
except:
pass
if has_mpi:
nngt.set_config("mpi", True)
assert nngt.get_config("mpi")
assert not nngt.get_config("multithreading")
assert nngt.get_config("omp") == 1
# key error
key_error = False
try:
nngt.set_config("random_entry", "plop")
except KeyError:
key_error = True
assert key_error
# except for palettes
nngt.set_config("palette_continuous", "viridis")
nngt.set_config("palette_discrete", "Set2")
# restore old config
nngt.set_config(old_cfg)
@pytest.mark.mpi_skip
def test_node_creation():
'''
When making graphs, test node creation function.
'''
g = nngt.Graph(100, name="new_node_test")
assert g.node_nb() == 100, \
"Error on '{}': invalid initial nodes ({} vs {} expected).".format(
g.name, g.node_nb(), 100)
n = g.new_node()
assert g.node_nb() == 101 and n == 100, \
"Error on '{}': ({}, {}) vs (101, 100) expected.".format(
g.name, g.node_nb(), n)
nn = g.new_node(2)
assert g.node_nb() == 103 and tuple(nn) == (101, 102), \
"Error on '{}': ({}, {}, {}) vs (103, 101, 102) expected.".format(
g.name, g.node_nb(), nn[0], nn[1])
@pytest.mark.mpi_skip
def test_edge_creation():
''' Check edge checks '''
num_nodes = 10
# DIRECTED
edges = [(0, 1), (2, 4)]
g = nngt.Graph(num_nodes)
g.new_edges(edges)
# empty edge list
assert not g.new_edges([])
# all following should trigger an error
for e in [(11, 10), (4, 4), (0, 1)]:
with pytest.raises(InvalidArgument):
g.new_edge(*e)
# all following should also trigger an error
lst_edges = [
[(1, 1), (0, 2), (3, 4)], # self-loop
[(9, 4), (5, 6), (9, 4)], # duplicate
[(2, 4), (3, 4), (7, 2)], # existing
[(20, 1), (4, 8), (3, 2)], # out-of-range
]
for elist in lst_edges:
with pytest.raises(InvalidArgument):
g.new_edges(elist)
# check specific filters
# self-loop
with pytest.raises(InvalidArgument):
g.new_edges(lst_edges[0], check_duplicates=False, check_existing=False)
# duplicate
with pytest.raises(InvalidArgument):
g.new_edges(lst_edges[1], check_duplicates=True,
check_self_loops=False, check_existing=False)
# existing
with pytest.raises(InvalidArgument):
g.new_edges(lst_edges[2], check_self_loops=False)
# out-of-range
with pytest.raises(InvalidArgument):
g.new_edges(lst_edges[3], check_self_loops=False, check_existing=False)
# working calls
g.new_edge(0, 1, ignore=True)
assert g.edge_id((0, 1)) == 0
g.set_weights(5.)
g.new_edge(0, 1, attributes={"weight": 3.}, ignore=True)
assert g.get_weights(edges=(0, 1)) == 5
g.new_edges(lst_edges[0], check_self_loops=False)
assert g.edge_nb() == 5
for elist in lst_edges[:-1]:
g.new_edges(elist, ignore_invalid=True)
assert g.edge_nb() == 8
# UNDIRECTED
g = nngt.Graph(num_nodes, directed=False)
g.new_edges(edges)
# all following should trigger an error
for e in [(1, 0), (0, 1)]:
with pytest.raises(InvalidArgument):
g.new_edge(*e)
# all following should also trigger an error
lst_edges = [
[(1, 1), (0, 2), (3, 4)], # self-loop
[(9, 4), (5, 6), (9, 4)], # duplicate
[(2, 4), (3, 4), (7, 2)], # existing
[(4, 2), (4, 8), (3, 2)], # existing 2
]
for elist in lst_edges:
with pytest.raises(InvalidArgument):
g.new_edges(elist)
# working calls
g.new_edge(0, 1, ignore=True)
assert g.edge_id((0, 1)) == 0
g.set_weights(5.)
g.new_edge(1, 0, attributes={"weight": 3.}, ignore=True)
assert g.get_weights(edges=(0, 1)) == 5
g.new_edges(lst_edges[0], check_self_loops=False)
assert g.edge_nb() == 5
for elist in lst_edges:
g.new_edges(elist, ignore_invalid=True)
assert g.edge_nb() == 10
@pytest.mark.mpi_skip
def test_has_edges_edge_id():
''' Test the ``has_edge`` and ``edge_id`` methods '''
num_nodes = 10
# DIRECTED
edges = [(0, 1), (2, 4)]
g = nngt.Graph(num_nodes)
g.new_edges(edges)
for i, e in enumerate(edges):
assert g.has_edge(e)
assert g.edge_id(e) == i
# UNDIRECTED
g = nngt.Graph(num_nodes, directed=False)
g.new_edges(edges)
for i, e in enumerate(edges):
assert g.has_edge(e)
assert g.edge_id(e) == i
assert g.has_edge(e[::-1])
assert g.edge_id(e[::-1]) == i
def test_new_node_attr():
'''
Test node creation with attributes.
'''
shape = nngt.geometry.Shape.rectangle(1000., 1000.)
g = nngt.SpatialGraph(100, shape=shape, name="new_node_spatial")
assert g.node_nb() == 100, \
"Error on '{}': invalid initial nodes ({} vs {} expected).".format(
g.name, g.node_nb(), 100)
n = g.new_node(positions=[(0, 0)])
assert np.all(np.isclose(g.get_positions(n), (0, 0), tolerance)), \
"Error on '{}': last position is ({}, {}) vs (0, 0) expected.".format(
g.name, *g.get_positions(n))
@pytest.mark.mpi_skip
def test_graph_copy():
'''
Test partial and full graph copy.
'''
# partial copy
# non-spatial graph
avg = 20
std = 4
g = ng.gaussian_degree(avg, std, nodes=100)
h = nngt.Graph(copy_graph=g)
assert g.node_nb() == h.node_nb()
assert g.edge_nb() == h.edge_nb()
assert np.array_equal(g.edges_array, h.edges_array)
# spatial network
pop = nngt.NeuralPop.exc_and_inhib(100)
shape = nngt.geometry.Shape.rectangle(1000., 1000.)
g = ng.gaussian_degree(avg, std, population=pop, shape=shape,
name="new_node_spatial")
h = nngt.Graph(copy_graph=g)
assert g.node_nb() == h.node_nb()
assert g.edge_nb() == h.edge_nb()
assert np.array_equal(g.edges_array, h.edges_array)
assert not h.is_network()
assert not h.is_spatial()
# full copy
rng = np.random.default_rng()
g.set_weights(rng.uniform(0, 10, g.edge_nb()))
g.new_node_attribute("plop", "int", rng.integers(1, 50, g.node_nb()))
g.new_node_attribute("bip", "double", rng.uniform(0, 1, g.node_nb()))
g.new_edge_attribute("test", "int", rng.integers(1, 200, g.edge_nb()))
copy = g.copy()
assert g.node_nb() == copy.node_nb()
assert g.edge_nb() == copy.edge_nb()
assert np.array_equal(g.edges_array, copy.edges_array)
for k, v in g.edge_attributes.items():
npt.assert_array_equal(v, copy.edge_attributes[k])
for k, v in g.node_attributes.items():
npt.assert_array_equal(v, copy.node_attributes[k])
assert g.population == copy.population
assert g.population is not copy.population
assert g.shape == copy.shape
assert g.shape is not copy.shape
# check that undirected graph stays undirected
g = ng.erdos_renyi(nodes=100, avg_deg=10, directed=False)
h = g.copy()
assert g.is_directed() == h.is_directed() == False
# eid is protected and should not be copied to a visible edge attribute
assert "eid" not in h.edge_attributes
def test_degrees_neighbors():
'''
Check ``Graph.get_degrees`` method.
'''
edge_list = [(0, 1), (0, 2), (0, 3), (1, 3), (3, 2), (3, 4), (4, 2)]
weights = [0.54881, 0.71518, 0.60276, 0.54488, 0.42365, 0.64589, 0.43758]
out_degrees = np.array([3, 1, 0, 2, 1])
in_degrees = np.array([0, 1, 3, 2, 1])
tot_degrees = in_degrees + out_degrees
out_strengths = np.array([1.86675, 0.54488, 0, 1.06954, 0.43758])
in_strengths = np.array([0, 0.54881, 1.57641, 1.14764, 0.64589])
tot_strengths = in_strengths + out_strengths
# DIRECTED
g = nngt.Graph(5, directed=True)
g.new_edges(edge_list, attributes={"weight": weights})
assert np.all(g.get_degrees(mode="in") == in_degrees)
assert np.all(g.get_degrees(mode="out") == out_degrees)
assert np.all(g.get_degrees() == tot_degrees)
assert np.all(
np.isclose(g.get_degrees(mode="in", weights=True), in_strengths))
assert np.all(
np.isclose(g.get_degrees(mode="out", weights=True), out_strengths))
assert np.all(np.isclose(g.get_degrees(weights="weight"), tot_strengths))
assert g.neighbours(3, "in") == {0, 1}
assert g.neighbours(3, "out") == {2, 4}
assert g.neighbours(3, "all") == {0, 1, 2, 4}
# UNDIRECTED
g = nngt.Graph(5, directed=False)
g.new_edges(edge_list, attributes={"weight": weights})
assert np.all(g.get_degrees(mode="in") == tot_degrees)
assert np.all(g.get_degrees(mode="out") == tot_degrees)
assert np.all(g.get_degrees() == tot_degrees)
assert np.all(
np.isclose(g.get_degrees(mode="in", weights=True), tot_strengths))
assert np.all(
np.isclose(g.get_degrees(mode="out", weights=True), tot_strengths))
assert np.all(np.isclose(g.get_degrees(weights="weight"), tot_strengths))
assert g.neighbours(3, "in") == {0, 1, 2, 4}
assert g.neighbours(3, "out") == {0, 1, 2, 4}
assert g.neighbours(3, "all") == {0, 1, 2, 4}
def test_directed_adjacency():
''' Check directed adjacency matrix '''
num_nodes = 5
edge_list = [(0, 1), (0, 3), (1, 3), (2, 0), (3, 2), (3, 4), (4, 2)]
weights = [0.54881, 0.71518, 0.60276, 0.54488, 0.42365, 0.64589, 0.43758]
etypes = [-1, 1, 1, -1, -1, 1, 1]
g = nngt.Graph(num_nodes)
g.new_edges(edge_list, attributes={"weight": weights})
g.new_edge_attribute("type", "int", values=etypes)
adj_mat = np.array([
[0, 1, 0, 1, 0],
[0, 0, 0, 1, 0],
[1, 0, 0, 0, 0],
[0, 0, 1, 0, 1],
[0, 0, 1, 0, 0]
])
assert np.all(np.isclose(g.adjacency_matrix(weights=False).todense(),
adj_mat))
w_mat = np.array([
[0, 0.54881, 0, 0.71518, 0 ],
[0, 0, 0, 0.60276, 0 ],
[0.54488, 0, 0, 0, 0 ],
[0, 0, 0.42365, 0, 0.64589],
[0, 0, 0.43758, 0, 0]
])
assert np.all(np.isclose(g.adjacency_matrix(weights=True).todense(),
w_mat))
# for typed edges
tpd_mat = np.array([
[ 0, -1, 0, 1, 0],
[ 0, 0, 0, 1, 0],
[-1, 0, 0, 0, 0],
[ 0, 0, -1, 0, 1],
[ 0, 0, 1, 0, 0]
])
assert np.all(np.isclose(g.adjacency_matrix(types=True).todense(),
tpd_mat))
wt_mat = np.array([
[ 0, -0.54881, 0, 0.71518, 0 ],
[ 0, 0, 0, 0.60276, 0 ],
[-0.54488, 0, 0, 0, 0 ],
[ 0, 0, -0.42365, 0, 0.64589],
[ 0, 0, 0.43758, 0, 0]
])
assert np.all(np.isclose(
g.adjacency_matrix(types=True, weights=True).todense(), wt_mat))
# for Network and node attribute type
num_nodes = 5
edge_list = [(0, 1), (0, 3), (1, 3), (2, 0), (3, 2), (3, 4), (4, 2)]
weights = [0.54881, 0.71518, 0.60276, 0.54488, 0.42365, 0.64589, 0.43758]
inh = nngt.NeuralGroup([0, 2, 4], neuron_type=-1, name="inh")
exc = nngt.NeuralGroup([1, 3], neuron_type=1, name="exc")
pop = nngt.NeuralPop.from_groups((inh, exc), with_models=False)
net = nngt.Network(population=pop)
net.new_edges(edge_list, attributes={"weight": weights})
g = nngt.Graph(num_nodes)
g.new_node_attribute("type", "int", values=[-1, 1, -1, 1, -1])
g.new_edges(edge_list, attributes={"weight": weights})
tpd_mat = np.array([
[ 0, -1, 0, -1, 0],
[ 0, 0, 0, 1, 0],
[-1, 0, 0, 0, 0],
[ 0, 0, 1, 0, 1],
[ 0, 0, -1, 0, 0]
])
assert np.all(np.isclose(net.adjacency_matrix(types=True).todense(),
tpd_mat))
assert np.all(np.isclose(g.adjacency_matrix(types=True).todense(),
tpd_mat))
wt_mat = np.array([
[ 0, -0.54881, 0, -0.71518, 0 ],
[ 0, 0, 0, 0.60276, 0 ],
[-0.54488, 0, 0, 0, 0 ],
[ 0, 0, 0.42365, 0, 0.64589],
[ 0, 0, -0.43758, 0, 0]
])
assert np.all(np.isclose(
net.adjacency_matrix(types=True, weights=True).todense(), wt_mat))
assert np.all(np.isclose(
g.adjacency_matrix(types=True, weights=True).todense(), wt_mat))
def test_undirected_adjacency():
''' Check undirected adjacency matrix '''
num_nodes = 5
edge_list = [(0, 1), (0, 3), (1, 3), (2, 0), (3, 2), (3, 4), (4, 2)]
weights = [0.54881, 0.71518, 0.60276, 0.54488, 0.42365, 0.64589, 0.43758]
etypes = [-1, 1, 1, -1, -1, 1, 1]
g = nngt.Graph(num_nodes, directed=False)
g.new_edges(edge_list, attributes={"weight": weights})
g.new_edge_attribute("type", "int", values=etypes)
adj_mat = np.array([
[0, 1, 1, 1, 0],
[1, 0, 0, 1, 0],
[1, 0, 0, 1, 1],
[1, 1, 1, 0, 1],
[0, 0, 1, 1, 0]
])
assert np.all(np.isclose(g.adjacency_matrix(weights=False).todense(),
adj_mat))
w_mat = np.array([
[0, 0.54881, 0.54488, 0.71518, 0 ],
[0.54881, 0, 0, 0.60276, 0 ],
[0.54488, 0, 0, 0.42365, 0.43758],
[0.71518, 0.60276, 0.42365, 0, 0.64589],
[0, 0, 0.43758, 0.64589, 0 ]
])
assert np.all(np.isclose(g.adjacency_matrix(weights=True).todense(),
w_mat))
# for typed edges
tpd_mat = np.array([
[ 0, -1, -1, 1, 0],
[-1, 0, 0, 1, 0],
[-1, 0, 0, -1, 1],
[ 1, 1, -1, 0, 1],
[ 0, 0, 1, 1, 0]
])
assert np.all(np.isclose(g.adjacency_matrix(types=True).todense(),
tpd_mat))
wt_mat = np.array([
[ 0, -0.54881, -0.54488, 0.71518, 0 ],
[-0.54881, 0, 0, 0.60276, 0 ],
[-0.54488, 0, 0, -0.42365, 0.43758],
[ 0.71518, 0.60276, -0.42365, 0, 0.64589],
[ 0, 0, 0.43758, 0.64589, 0 ]
])
assert np.all(np.isclose(
g.adjacency_matrix(types=True, weights=True).todense(), wt_mat))
@pytest.mark.mpi_skip
def test_get_edges():
''' Check that correct edges are returned '''
# directed
g = nngt.Graph(4, directed=True)
edges = [(0, 1), (1, 0), (1, 2), (2, 3)]
g.new_edges(edges)
def to_set(ee):
return {tuple(e) for e in ee}
assert g.get_edges(source_node=0) == [(0, 1)]
assert g.get_edges(target_node=2) == [(1, 2)]
assert to_set(g.get_edges(source_node=[0, 1])) == to_set(edges[:3])
assert to_set(g.get_edges(target_node=[0, 1])) == to_set(edges[:2])
assert to_set(g.get_edges(source_node=[0, 2],
target_node=[0, 1])) == {(0, 1)}
# undirected
g = nngt.Graph(4, directed=False)
edges = [(0, 1), (1, 2), (2, 3)]
g.new_edges(edges)
res = {(0, 1), (1, 2)}
assert g.get_edges(source_node=0) == [(0, 1)]
assert to_set(g.get_edges(target_node=2)) == {(1, 2), (2, 3)}
assert to_set(g.get_edges(source_node=[0, 1])) == res
assert to_set(g.get_edges(target_node=[0, 1])) == res
assert to_set(g.get_edges(source_node=[0, 2],
target_node=[0, 1])) == res
assert to_set(g.get_edges(source_node=0, target_node=1)) == {(0, 1)}
assert to_set(g.get_edges(source_node=0, target_node=[0, 1])) == {(0, 1)}
@pytest.mark.mpi_skip
def test_delete():
''' Test node and edge deletion '''
mat = np.array([
[0., 0.5, 0., 0.2, 0., 1. ],
[0., 0., 0.5, 0., 0.3, 0. ],
[0.1, 0., 0., 0., 1., 0. ],
[0., 0.2, 1., 0., 0., 0. ],
[0.5, 0., 0., 0., 0., 0.5],
[0., 0.1, 0., 0., 0., 0. ],
])
num_nodes = 6
num_edges = 12
in_deg = [2, 3, 2, 1, 2, 2]
out_deg = [3, 2, 2, 2, 2, 1]
# make positions and structure
rng = np.random.default_rng()
positions = rng.uniform(-5, 5, size=(num_nodes, 2))
g1 = nngt.Group([1, 2, 3])
g2 = nngt.Group([0, 4, 5])
struct = nngt.Structure.from_groups([g1, g2], ["g1", "g2"])
g = nngt.Graph.from_matrix(mat, positions=positions, structure=struct)
g.new_node_attribute("test", "int", values=list(range(num_nodes)))
assert num_edges == g.edge_nb()
# delete one edge (eid = 2)
edge = (0, 5)
in_deg[5] -= 1
out_deg[0] -= 1
g.delete_edges(edge)
num_edges -= 1
assert g.edge_nb() == num_edges
assert len(g.get_weights()) == num_edges
assert np.array_equal(in_deg, g.get_degrees("in"))
assert np.array_equal(out_deg, g.get_degrees("out"))
mat[edge] = 0
adj = g.adjacency_matrix(weights=True, mformat="dense")
assert np.all(np.isclose(mat, adj))
# delete several edges (eids = (3, 6))
edges = [(1, 4), (3, 1)]
g.delete_edges(edges)
in_deg[4] -= 1
out_deg[1] -= 1
in_deg[1] -= 1
out_deg[3] -= 1
num_edges -= len(edges)
assert g.edge_nb() == num_edges
assert len(g.get_weights()) == num_edges
assert np.array_equal(in_deg, g.get_degrees("in"))
assert np.array_equal(out_deg, g.get_degrees("out"))
for e in edges:
mat[e] = 0
adj = g.adjacency_matrix(weights=True, mformat="dense")
assert np.all(np.isclose(mat, adj))
# deleting one node
g.delete_nodes([0])
adj = g.adjacency_matrix(weights=True, mformat="dense")
assert g.node_nb() == 5
assert np.array_equal(adj, mat[1:, 1:])
assert np.array_equal(g.node_attributes["test"], [1, 2, 3, 4, 5])
assert set(g.structure["g2"].ids) == {3, 4}
assert np.array_equal(g.get_positions(), positions[1:])
# delete two nodes
g.delete_nodes([1, 2])
assert g.node_nb() == 3
adj = g.adjacency_matrix(weights=True, mformat="dense")
assert np.array_equal(adj, mat[[1, 4, 5]][:, [1, 4, 5]])
assert np.array_equal(g.node_attributes["test"], [1, 4, 5])
assert set(g.structure["g1"].ids) == {0}
assert set(g.structure["g2"].ids) == {1, 2}
assert np.array_equal(g.get_positions(), positions[[1, 4, 5]])
# readd nodes
g.new_node(2, attributes={"test": [-1, 2]}, positions=[(-2, 1), (0.5, 3)])
assert g.node_nb() == 5
assert np.array_equal(g.node_attributes["test"], [1, 4, 5, -1, 2])
# readd edges
g.new_edges([(1, 4), (4, 3)])
assert g.edge_nb() == 4
assert np.array_equal(g.edges_array, [(1, 2), (2, 0), (1, 4), (4, 3)])
assert np.all(np.isclose(
g.get_weights(), [0.5, 0.1, 1., 1.], equal_nan=True))
# test delete from get_edges (issue #136)
edges = g.get_edges()
g.delete_edges(edges[:2])
# test copy after edge deletion
h = g.copy()
assert np.all(np.isclose(h.get_weights(), g.get_weights()))
assert np.all(np.isclose(h.edge_attributes["distance"],
g.edge_attributes["distance"]))
def test_to_undirected():
mat = np.array([
[0, 2., 0.5, 0],
[0, 0, 1., 0],
[1.5, 0, 0, 1],
[0, 1, 0.5, 0]
])
g = nngt.Graph.from_matrix(mat)
g.new_node_attribute("test", "int", [10, 20, 30, 40])
g.new_node_attribute("alph", "string", ["d", "c", "b", "a"])
g.new_edge_attribute("rnd", "int", [2, 6, 8, 4, 5, 3, 9])
g.new_edge_attribute("alph", "string",
["a", "e", "i", "o", "u", "y", "aa"])
# undirected sum
u = g.to_undirected()
assert np.array_equal(u.node_attributes["test"], g.node_attributes["test"])
assert list(u.node_attributes["alph"]) == list(g.node_attributes["alph"])
assert set(u.edge_attributes) == {"weight", "rnd"}
assert np.all(np.isclose(
mat + mat.T, u.adjacency_matrix(weights="weight").todense()
))
assert np.array_equal(u.edge_attributes["rnd"], [2, 10, 8, 3, 14])
# make spatial
pos = nngt._rng.uniform(size=(g.node_nb(), 2))
g.make_spatial(g, positions=pos)
# undirected max
u = g.to_undirected("max")
m = np.maximum(mat, mat.T)
assert np.all(np.isclose(
m, u.adjacency_matrix(weights="weight").todense()
))
assert np.array_equal(u.edge_attributes["rnd"], [2, 6, 8, 3, 9])
# make network
pop = nngt.NeuralPop.uniform(g.node_nb())
g.make_network(g, pop)
# undirected min
u = g.to_undirected("min")
nnz = np.where(np.multiply(mat, mat.T))
m = mat + mat.T
m[nnz] = np.minimum(mat[nnz], mat.T[nnz])
assert np.all(np.isclose(
m, u.adjacency_matrix(weights="weight").todense()
))
assert np.array_equal(u.edge_attributes["rnd"], [2, 4, 8, 3, 5])
# undirected mean
u = g.to_undirected("mean")
nnz = np.where(np.multiply(mat, mat.T))
m = mat + mat.T
m[nnz] = 0.5*(mat[nnz] + mat.T[nnz])
assert np.all(np.isclose(
m, u.adjacency_matrix(weights="weight").todense()
))
assert np.array_equal(u.edge_attributes["rnd"], [2, 5, 8, 3, 7])
# undirected mean/max
u = g.to_undirected({"weight": "mean", "rnd": "max"})
assert np.all(np.isclose(
m, u.adjacency_matrix(weights="weight").todense()
))
assert np.array_equal(u.edge_attributes["rnd"], [2, 6, 8, 3, 9])
# for an unweighted graph
g = nngt.Graph.from_matrix(mat, weighted=False)
u = g.to_undirected()
assert not u.edge_attributes
m = mat + mat.T
nnz = np.where(m)
m[nnz] = 1
assert np.array_equal(u.adjacency_matrix().todense(), m)
@pytest.mark.mpi_skip
def test_density():
# directed
num_nodes = 3
g = nngt.Graph(num_nodes)
edges = [(0, 1), (1, 2), (2, 0)]
g.new_edges(edges)
assert np.isclose(g.get_density(ignore_loops=True), 1/3)
assert np.isclose(g.get_density(ignore_loops=False), 0.5)
# undirected
g = nngt.Graph(num_nodes, directed=False)
edges = [(0, 1), (1, 2), (2, 0)]
g.new_edges(edges)
assert np.isclose(g.get_density(ignore_loops=True), 0.5)
assert np.isclose(g.get_density(ignore_loops=False), 1)
# ---------- #
# Test suite #
# ---------- #
if __name__ == "__main__":
test_directed_adjacency()
test_undirected_adjacency()
test_config()
test_new_node_attr()
test_graph_copy()
test_degrees_neighbors()
test_get_edges()
test_to_undirected()
if not nngt.get_config('mpi'):
test_node_creation()
test_edge_creation()
test_has_edges_edge_id()
test_delete()
test_density()