Source code for wbia.algo.graph.tests.test_neg_metagraph
# -*- coding: utf-8 -*-
"""
TODO: These tests are good and important to run.
Ensure they are run via run_tests even though they are not doctests.
Consider moving to pytest and using xdoctest (because regular doctest does not
accept the syntax of IBEIS doctests)
"""
from __future__ import absolute_import, division, print_function
import utool as ut
(print, rrr, profile) = ut.inject2(__name__)
[docs]def test_neg_metagraph_simple_add_remove():
"""
Test that the negative metagraph tracks the number of negative edges
between PCCs through non-label-changing operations
"""
from wbia.algo.graph import demo
from wbia.algo.graph.state import POSTV, NEGTV, INCMP, UNREV, UNKWN # NOQA
# Create a graph with 5-sized CCs, with 3-pos-redun, and no negative edges
infr = demo.demodata_infr(
num_pccs=2, pcc_size=5, pos_redun=3, ignore_pair=True, infer=True
)
cc_a, cc_b, cc_c, cc_d = infr.positive_components()
a1, a2, a3, a4, a5 = cc_a
b1, b2, b3, b4, b5 = cc_b
nmg = infr.neg_metagraph
# Check there are 4 meta-nodes and no edges
assert nmg.number_of_edges() == 0
assert nmg.number_of_nodes() == 4
# Should add 1 edge to the negative metagraph
u, v = a1, b1
infr.add_feedback((u, v), NEGTV)
nid1, nid2 = infr.node_labels(u, v)
assert nmg.edges[nid1, nid2]['weight'] == 1
assert nmg.number_of_edges() == 1
assert nmg.number_of_nodes() == 4
# Adding a second time should do nothing
edge = a1, b1
infr.add_feedback(edge, NEGTV)
name_edge = infr.node_labels(*edge)
assert nmg.edges[name_edge]['weight'] == 1
assert nmg.number_of_edges() == 1
assert nmg.number_of_nodes() == 4
# But adding a second between different nodes will increase the weight
edge = a1, b2
infr.add_feedback(edge, NEGTV)
name_edge = infr.node_labels(*edge)
assert nmg.edges[name_edge]['weight'] == 2
assert nmg.number_of_edges() == 1
assert nmg.number_of_nodes() == 4
infr.add_feedback((u, v), NEGTV)
assert nmg.edges[name_edge]['weight'] == 2
# Removing or relabeling the edge will decrease the weight
infr.add_feedback((a1, b2), INCMP)
assert nmg.edges[name_edge]['weight'] == 1
# And removing all will remove the negative edge
infr.add_feedback((a1, b1), INCMP)
assert not nmg.has_edge(*name_edge)
infr.assert_neg_metagraph()
[docs]def test_neg_metagraph_merge():
"""
Test that the negative metagraph tracks the number of negative edges
between PCCs through label-changing merge operations
"""
from wbia.algo.graph import demo
from wbia.algo.graph.state import POSTV, NEGTV, INCMP, UNREV, UNKWN # NOQA
# Create a graph with 4 CCs, with 3-pos-redun, and no negative edges
infr = demo.demodata_infr(
num_pccs=4, pcc_size=5, pos_redun=3, ignore_pair=True, infer=True
)
cc_a, cc_b, cc_c, cc_d = infr.positive_components()
a1, a2, a3, a4, a5 = cc_a
b1, b2, b3, b4, b5 = cc_b
c1, c2, c3, c4, c5 = cc_c
d1, d2, d3, d4, d5 = cc_d
nmg = infr.neg_metagraph
# Add three negative edges between a and b
# one between (a, c), (b, d), (a, d), and (c, d)
A, B, C, D = infr.node_labels(a1, b1, c1, d1)
infr.add_feedback((a1, b1), NEGTV)
infr.add_feedback((a2, b2), NEGTV)
infr.add_feedback((a3, b3), NEGTV)
infr.add_feedback((a4, c4), NEGTV)
infr.add_feedback((b4, d4), NEGTV)
infr.add_feedback((c1, d1), NEGTV)
infr.add_feedback((a4, d4), NEGTV)
assert nmg.edges[(A, B)]['weight'] == 3
assert nmg.edges[(A, C)]['weight'] == 1
assert (B, C) not in nmg.edges
assert nmg.edges[(A, D)]['weight'] == 1
assert nmg.edges[(B, D)]['weight'] == 1
assert nmg.number_of_edges() == 5
assert nmg.number_of_nodes() == 4
# Now merge A and B
infr.add_feedback((a1, b1), POSTV)
AB = infr.node_label(a1)
# The old meta-nodes should not be combined into AB
assert infr.node_label(b1) == AB
assert A != B
assert A == AB or A not in nmg.nodes
assert B == AB or B not in nmg.nodes
# Should have combined weights from (A, D) and (B, D)
# And (A, C) should be brought over as-is
assert nmg.edges[(AB, D)]['weight'] == 2
assert nmg.edges[(AB, C)]['weight'] == 1
# should not have a self-loop weight weight 2
# (it decreased because we changed a previously neg edge to pos)
assert nmg.edges[(AB, AB)]['weight'] == 2
assert len(list(nmg.selfloop_edges())) == 1
# nothing should change between C and D
assert nmg.edges[(C, D)]['weight'] == 1
# Should decrease number of nodes and edges
assert nmg.number_of_nodes() == 3
assert nmg.number_of_edges() == 4
infr.assert_neg_metagraph()
# Additional merge
infr.add_feedback((c2, d2), POSTV)
CD = infr.node_label(c1)
infr.assert_neg_metagraph()
assert nmg.number_of_nodes() == 2
assert nmg.number_of_edges() == 3
assert nmg.edges[(CD, CD)]['weight'] == 1
assert nmg.edges[(AB, CD)]['weight'] == 3
assert nmg.edges[(AB, AB)]['weight'] == 2
# Yet another merge
infr.add_feedback((a1, c1), POSTV)
ABCD = infr.node_label(c1)
assert nmg.number_of_nodes() == 1
assert nmg.number_of_edges() == 1
nmg.edges[(ABCD, ABCD)]['weight'] = 6
infr.assert_neg_metagraph()
[docs]def test_neg_metagraph_split_neg():
"""
Test that the negative metagraph tracks the number of negative edges
between PCCs through label-changing split operations
"""
from wbia.algo.graph import demo
from wbia.algo.graph.state import POSTV, NEGTV, INCMP, UNREV, UNKWN # NOQA
# Create a graph with 4 CCs, with 3-pos-redun, and no negative edges
infr = demo.demodata_infr(
num_pccs=4, pcc_size=5, pos_redun=3, ignore_pair=True, infer=True
)
nmg = infr.neg_metagraph
assert nmg.number_of_nodes() != infr.neg_graph.number_of_nodes()
assert nmg.number_of_edges() == 0
# remove all positive edges
for edge in list(infr.pos_graph.edges()):
infr.add_feedback(edge, NEGTV)
# metagraph should not be isomorphic to infr.neg_graph
assert nmg.number_of_nodes() == infr.neg_graph.number_of_nodes()
assert nmg.number_of_edges() > 0
assert nmg.number_of_edges() == infr.neg_graph.number_of_edges()
infr.assert_neg_metagraph()
[docs]def test_neg_metagraph_split_incomp():
from wbia.algo.graph import demo
from wbia.algo.graph.state import POSTV, NEGTV, INCMP, UNREV, UNKWN # NOQA
infr = demo.demodata_infr(
num_pccs=4, pcc_size=5, pos_redun=3, ignore_pair=True, infer=True
)
nmg = infr.neg_metagraph
assert nmg.number_of_nodes() < infr.neg_graph.number_of_nodes()
assert nmg.number_of_edges() == 0
# remove all positive edges
for edge in list(infr.pos_graph.edges()):
infr.add_feedback(edge, INCMP)
# metagraph should not be isomorphic to infr.neg_graph
assert nmg.number_of_nodes() == infr.neg_graph.number_of_nodes()
assert nmg.number_of_edges() == 0
infr.assert_neg_metagraph()
[docs]def test_neg_metagraph_split_and_merge():
"""
Test that the negative metagraph tracks the number of negative edges
between PCCs through label-changing split and merge operations
"""
from wbia.algo.graph import demo
from wbia.algo.graph.state import POSTV, NEGTV, INCMP, UNREV, UNKWN # NOQA
# Create a graph with 4 CCs, with 3-pos-redun, and no negative edges
infr = demo.demodata_infr(
num_pccs=4, pcc_size=5, pos_redun=3, ignore_pair=True, infer=True
)
cc_a, cc_b, cc_c, cc_d = infr.positive_components()
a1, a2, a3, a4, a5 = cc_a
b1, b2, b3, b4, b5 = cc_b
c1, c2, c3, c4, c5 = cc_c
d1, d2, d3, d4, d5 = cc_d
nmg = infr.neg_metagraph
# Add three negative edges between a and b
# one between (a, c), (b, d), (a, d), and (c, d)
A, B, C, D = infr.node_labels(a1, b1, c1, d1)
infr.add_feedback((a1, b1), NEGTV)
infr.add_feedback((a2, b2), NEGTV)
infr.add_feedback((a3, b3), NEGTV)
infr.add_feedback((a4, c4), NEGTV)
infr.add_feedback((b4, d4), NEGTV)
infr.add_feedback((c1, d1), NEGTV)
infr.add_feedback((a4, d4), NEGTV)
assert nmg.edges[(A, B)]['weight'] == 3
assert nmg.edges[(A, C)]['weight'] == 1
assert (B, C) not in nmg.edges
assert nmg.edges[(A, D)]['weight'] == 1
assert nmg.edges[(B, D)]['weight'] == 1
assert nmg.number_of_edges() == 5
assert nmg.number_of_nodes() == 4
# merge A and B
infr.add_feedback((a1, b1), POSTV)
assert nmg.number_of_edges() == 4
assert nmg.number_of_nodes() == 3
AB = infr.node_label(a1)
assert nmg.edges[(AB, AB)]['weight'] == 2
# split A and B
# the number of nodes should increase, but the edges should stay the
# same because we added an incmp edge
infr.add_feedback((a1, b1), INCMP)
assert nmg.number_of_edges() == 5
assert nmg.number_of_nodes() == 4
assert nmg.edges[(A, B)]['weight'] == 2
infr.assert_neg_metagraph()
# remove all positive edges
for edge in list(infr.pos_graph.edges()):
infr.add_feedback(edge, INCMP)
# metagraph should not be isomorphic to infr.neg_graph
assert nmg.number_of_nodes() == infr.neg_graph.number_of_nodes()
assert nmg.number_of_edges() == infr.neg_graph.number_of_edges()
infr.assert_neg_metagraph()