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()
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 = infr.positive_components() a1, a2, a3, a4, a5 = cc_a b1, b2, b3, b4, b5 = cc_b nmg = infr.neg_metagraph # Check there are 2 meta-nodes and no edges assert nmg.number_of_edges() == 0 assert nmg.number_of_nodes() == 2 # 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() == 2 # 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() == 2 # 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() == 2 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()
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()
def _test_pos_neg(): infr = demo.demodata_infr(num_pccs=0) # Make 3 inconsistent CCs infr.add_feedback((1, 2), POSTV) infr.add_feedback((2, 3), POSTV) infr.add_feedback((3, 4), POSTV) infr.add_feedback((4, 1), POSTV) infr.add_feedback((1, 3), NEGTV) # ----- infr.add_feedback((11, 12), POSTV) infr.add_feedback((12, 13), POSTV) infr.add_feedback((13, 11), NEGTV) # ----- infr.add_feedback((21, 22), POSTV) infr.add_feedback((22, 23), POSTV) infr.add_feedback((23, 21), NEGTV) # ----- # Fix inconsistency infr.add_feedback((23, 21), POSTV) # Merge inconsistent CCS infr.add_feedback((1, 11), POSTV) # Negative edge within an inconsistent CC infr.add_feedback((2, 13), NEGTV) # Negative edge external to an inconsistent CC infr.add_feedback((12, 21), NEGTV) # ----- # Make inconsistency from positive infr.add_feedback((31, 32), POSTV) infr.add_feedback((33, 34), POSTV) infr.add_feedback((31, 33), NEGTV) infr.add_feedback((32, 34), NEGTV) infr.add_feedback((31, 34), POSTV) # Fix everything infr.add_feedback((1, 3), POSTV) infr.add_feedback((2, 4), POSTV) infr.add_feedback((32, 34), POSTV) infr.add_feedback((31, 33), POSTV) infr.add_feedback((13, 11), POSTV) infr.add_feedback((23, 21), POSTV) infr.add_feedback((1, 11), NEGTV) logger.info('Final state:') logger.info(ut.repr4(sorted(infr.gen_edge_attrs('decision'))))
def _test_unrev_inference(): infr = demo.demodata_infr(num_pccs=0) # Make 2 consistent and 2 inconsistent CCs infr.add_feedback((1, 2), POSTV) infr.add_feedback((2, 3), POSTV) infr.add_feedback((3, 4), POSTV) infr.add_feedback((4, 1), POSTV) # ----- infr.add_feedback((11, 12), POSTV) infr.add_feedback((12, 13), POSTV) infr.add_feedback((13, 14), POSTV) infr.add_feedback((14, 11), POSTV) infr.add_feedback((12, 14), NEGTV) # ----- infr.add_feedback((21, 22), POSTV) infr.add_feedback((22, 23), POSTV) infr.add_feedback((23, 21), NEGTV) # ----- infr.add_feedback((31, 32), POSTV) infr.add_feedback((32, 33), POSTV) infr.add_feedback((33, 31), POSTV) infr.add_feedback((2, 32), NEGTV) infr.add_feedback((3, 33), NEGTV) infr.add_feedback((12, 21), NEGTV) # ----- # Incomparable within CCs logger.info('==========================') infr.add_feedback((1, 3), UNREV) infr.add_feedback((1, 4), UNREV) infr.add_feedback((1, 2), UNREV) infr.add_feedback((11, 13), UNREV) infr.add_feedback((11, 14), UNREV) infr.add_feedback((11, 12), UNREV) infr.add_feedback((1, 31), UNREV) infr.add_feedback((2, 32), UNREV) infr.add_feedback((12, 21), UNREV) infr.add_feedback((23, 21), UNREV) infr.add_feedback((12, 14), UNREV) logger.info('Final state:') logger.info(ut.repr4(sorted(infr.gen_edge_attrs('decision'))))
def demo_refresh(): r""" CommandLine: python -m wbia.algo.graph.refresh demo_refresh \ --num_pccs=40 --size=2 --show Example: >>> # ENABLE_DOCTEST >>> from wbia.algo.graph.refresh import * # NOQA >>> demo_refresh() >>> ut.show_if_requested() """ from wbia.algo.graph import demo demokw = ut.argparse_dict({'num_pccs': 50, 'size': 4}) refreshkw = ut.argparse_funckw(RefreshCriteria) # make an inference object infr = demo.demodata_infr(size_std=0, **demokw) edges = list(infr.dummy_verif.find_candidate_edges(K=100)) scores = np.array(infr.dummy_verif.predict_edges(edges)) sortx = scores.argsort()[::-1] edges = ut.take(edges, sortx) scores = scores[sortx] ys = infr.match_state_df(edges)[POSTV].values y_remainsum = ys[::-1].cumsum()[::-1] # Do oracle reviews and wait to converge refresh = RefreshCriteria(**refreshkw) xdata = [] pprob_any = [] rfrac_any = [] for count, (edge, y) in enumerate(zip(edges, ys)): refresh.add(y, user_id='user:oracle') rfrac_any.append(y_remainsum[count] / y_remainsum[0]) pprob_any.append(refresh.prob_any_remain()) xdata.append(count + 1) if refresh.check(): break xdata = xdata ydatas = ut.odict( [('Est. probability any remain', pprob_any), ('Fraction remaining', rfrac_any)] ) ut.quit_if_noshow() import wbia.plottool as pt pt.qtensure() from wbia.scripts.thesis import TMP_RC import matplotlib as mpl mpl.rcParams.update(TMP_RC) pt.multi_plot( xdata, ydatas, xlabel='# manual reviews', rcParams=TMP_RC, marker='', ylim=(0, 1), use_legend=False, ) demokw = ut.map_keys({'num_pccs': '#PCC', 'size': 'PCC size'}, demokw) thresh = refreshkw.pop('thresh') refreshkw['span'] = refreshkw.pop('window') pt.relative_text( (0.02, 0.58 + 0.0), ut.get_cfg_lbl(demokw, sep=' ')[1:], valign='bottom' ) pt.relative_text( (0.02, 0.68 + 0.0), ut.get_cfg_lbl(refreshkw, sep=' ')[1:], valign='bottom' ) legend = pt.gca().legend() legend.get_frame().set_alpha(1.0) pt.plt.plot([xdata[0], xdata[-1]], [thresh, thresh], 'g--', label='thresh')
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(nx.selfloop_edges(nmg))) == 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()
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()