Beispiel #1
0
def recolor_exi_graph(exi_graph, rootmost_nodes):
    node_dict = ut.nx_node_dict(exi_graph)
    for node in exi_graph.nodes():
        if node_dict[node]['root_specifiable']:
            node_dict[node]['color'] = [1, .7, .6]
    for node in rootmost_nodes:
        node_dict[node]['color'] = [1, 0, 0]
Beispiel #2
0
 def is_same(infr, aid_pairs):
     if infr.ibs is not None:
         return infr.ibeis_is_same(aid_pairs)
     node_dict = ut.nx_node_dict(infr.graph)
     nid1 = [node_dict[n1]['orig_name_label'] for n1, n2 in aid_pairs]
     nid2 = [node_dict[n2]['orig_name_label'] for n1, n2 in aid_pairs]
     return np.equal(nid1, nid2)
Beispiel #3
0
 def _get_truth(verif, edge):
     infr = verif.infr
     if edge in infr.edge_truth:
         return infr.edge_truth[edge]
     node_dict = ut.nx_node_dict(infr.graph)
     nid1 = node_dict[edge[0]]['orig_name_label']
     nid2 = node_dict[edge[1]]['orig_name_label']
     return POSTV if nid1 == nid2 else NEGTV
Beispiel #4
0
def get_edge_truth(infr, n1, n2):
    node_dict = ut.nx_node_dict(infr.graph)
    nid1 = node_dict[n1]['orig_name_label']
    nid2 = node_dict[n2]['orig_name_label']
    try:
        view1 = node_dict[n1]['viewpoint']
        view2 = node_dict[n2]['viewpoint']
        comparable = view1 in adjacent_views[view2]
    except KeyError:
        comparable = True
        # raise
    same = nid1 == nid2

    if not comparable:
        return 2
    else:
        return int(same)
Beispiel #5
0
    def show_exi_graph(inputs, inter=None):
        """
        CommandLine:
            python -m dtool.input_helpers TableInput.show_exi_graph --show

        Example:
            >>> # DISABLE_DOCTEST
            >>> from dtool.input_helpers import *  # NOQA
            >>> from dtool.example_depcache2 import *  # NOQA
            >>> depc = testdata_depc3()
            >>> import plottool as pt
            >>> # table = depc['smk_match']
            >>> table = depc['neighbs']
            >>> inputs = table.rootmost_inputs
            >>> print('inputs = %r' % (inputs,))
            >>> from plottool.interactions import ExpandableInteraction
            >>> inter = ExpandableInteraction(nCols=1)
            >>> inputs.show_exi_graph(inter=inter)
            >>> # FIXME; Expanding inputs can overspecify inputs
            >>> #inputs = inputs.expand_input(2)
            >>> #print('inputs = %r' % (inputs,))
            >>> #inputs.show_exi_graph(inter=inter)
            >>> #inputs = inputs.expand_input(1)
            >>> #inputs = inputs.expand_input(3)
            >>> #inputs = inputs.expand_input(2)
            >>> #inputs = inputs.expand_input(2)
            >>> #inputs = inputs.expand_input(1)
            >>> #print('inputs = %r' % (inputs,))
            >>> #inputs.show_exi_graph(inter=inter)
            >>> inter.start()
            >>> ut.show_if_requested()
        """
        import plottool as pt
        from plottool.interactions import ExpandableInteraction
        autostart = inter is None
        if inter is None:
            inter = ExpandableInteraction()
        tablename = inputs.table.tablename

        exi_graph = inputs.exi_graph.copy()
        recolor_exi_graph(exi_graph, inputs.exi_nodes())

        # Add numbering to indicate the input order
        node_dict = ut.nx_node_dict(exi_graph)
        for count, rmi in enumerate(inputs.rmi_list, start=0):
            if rmi.ismulti:
                node_dict[rmi.node]['label'] += ' #%d*' % (count, )
            else:
                node_dict[rmi.node]['label'] += ' #%d' % (count, )

        plot_kw = {'fontname': 'Ubuntu'}
        #inter.append_plot(
        #    ut.partial(pt.show_nx, G, title='Dependency Subgraph (%s)' % (tablename), **plot_kw))
        inter.append_plot(
            ut.partial(pt.show_nx,
                       exi_graph,
                       title='Expanded Input (%s)' % (tablename, ),
                       **plot_kw))
        if autostart:
            inter.start()
        return inter
Beispiel #6
0
def make_expanded_input_graph(graph, target):
    """
    Starting from the `target` property we trace all possible paths in the
    `graph` back to all sources.

    Args:
        graph (nx.DiMultiGraph): the dependency graph with a single source.
        target (str): a single target node in graph

    Notes:
        Each edge in the graph must have a `local_input_id` that defines the
        type of edge it is: (eg one-to-many, one-to-one, nwise/multi).

        # Step 1: Extracting the Relevant Subgraph
        We start by searching for all sources of the graph (we assume there is
        only one). Then we extract the subgraph defined by all edges between
        the sources and the target.  We augment this graph with a dummy super
        source `s` and super sink `t`. This allows us to associate an edge with
        the real source and sink.

        # Step 2: Trace all paths from `s` to `t`.
        Create a set of all paths from the source to the sink and accumulate
        the `local_input_id` of each edge along the path. This will uniquely
        identify each path. We use a hack to condense the accumualated ids in
        order to display them nicely.

        # Step 3: Create the new `exi_graph`
        Using the traced paths with ids we construct a new graph representing
        expanded inputs. The nodes in the original graph will be copied for each
        unique path that passes through the node. We identify these nodes using
        the accumulated ids built along the edges in our path set.  For each
        path starting from the target we add each node augmented with the
        accumulated ids on its output(?) edge. We also add the edges along
        these paths which results in the final `exi_graph`.

        # Step 4: Identify valid inputs candidates
        The purpose of this graph is to identify which inputs are needed
        to compute dependant properties. One valid set of inputs is all
        sources of the graph. However, sometimes it is preferable to specify
        a model that may have been trained from many inputs. Therefore any
        node with a one-to-many input edge may also be specified as an input.

        # Step 5: Identify root-most inputs
        The user will only specify one possible set of the inputs. We refer  to
        this set as the "root-most" inputs. This is a set of candiate nodes
        such that all paths from the sink to the super source are blocked.  We
        default to the set of inputs which results in the fewest dependency
        computations. However this is arbitary.

        The last step that is not represented here is to compute the order that
        the branches must be specified in when given to the depcache for a
        computation.

    Returns:
        nx.DiGraph: exi_graph: the expanded input graph

    Notes:
        All * nodes are defined to be distinct.
        TODO: To make a * node non-distinct it must be suffixed with an
        identifier.

    CommandLine:
        python -m dtool.input_helpers make_expanded_input_graph --show

    Example:
        >>> # ENABLE_DOCTEST
        >>> from dtool.input_helpers import *  # NOQA
        >>> from dtool.example_depcache2 import * # NOQA
        >>> depc = testdata_depc3()
        >>> table = depc['smk_match']
        >>> table = depc['vsone']
        >>> graph = table.depc.explicit_graph.copy()
        >>> target = table.tablename
        >>> exi_graph = make_expanded_input_graph(graph, target)
        >>> x = list(exi_graph.nodes())[0]
        >>> print('x = %r' % (x,))
        >>> ut.quit_if_noshow()
        >>> import plottool as pt
        >>> pt.show_nx(graph, fnum=1, pnum=(1, 2, 1))
        >>> pt.show_nx(exi_graph, fnum=1, pnum=(1, 2, 2))
        >>> ut.show_if_requested()
    """

    # FIXME: this does not work correctly when
    # The nesting of non-1-to-1 dependencies is greater than 2 (I think)
    # algorithm for finding inputs does not work.

    # FIXME: two vocabs have the same edge id, they should be the same in the
    # Expanded Input Graph as well. Their accum_id needs to be changed.

    def condense_accum_ids(rinput_path_id):
        # Hack to condense and consolidate graph sources
        prev = None
        compressed = []
        for item in rinput_path_id:
            if item == '1' and prev is not None:
                pass  # done append ones
            elif item != prev:
                compressed.append(item)
            prev = item
        #if len(compressed) > 1 and compressed[0] in ['1', '*']:
        if len(compressed) > 1 and compressed[0] == '1':
            compressed = compressed[1:]
        compressed = tuple(compressed)
        return compressed

    BIG_HACK = True

    #BIG_HACK = False

    def condense_accum_ids_stars(rinput_path_id):
        # Hack to condense and consolidate graph sources
        rcompressed = []
        has_star = False
        # Remove all but the final star (this is a really bad hack)
        for item in reversed(rinput_path_id):
            is_star = '*' in item
            if not (is_star and has_star):
                if not has_star:
                    rcompressed.append(item)
            has_star = has_star or is_star
        compressed = tuple(rcompressed[::-1])
        return compressed

    def accumulate_input_ids(edge_list):
        """
        python -m dtool.example_depcache2 testdata_depc4 --show
        """
        edge_data = ut.take_column(edge_list, 3)
        # We are accumulating local input ids
        toaccum_list_ = ut.dict_take_column(edge_data, 'local_input_id')
        if BIG_HACK and True:
            v_list = ut.take_column(edge_list, 1)
            # show the local_input_ids at the entire level
            pred_ids = ([[
                x['local_input_id']
                for x in list(graph.pred[node].values())[0].values()
            ] if len(graph.pred[node]) else [] for node in v_list])
            toaccum_list = [
                x + ':' + ';'.join(y) for x, y in zip(toaccum_list_, pred_ids)
            ]
        else:
            toaccum_list = toaccum_list_

        # Default dumb accumulation
        accum_ids_ = ut.cumsum(zip(toaccum_list), tuple())
        accum_ids = ut.lmap(condense_accum_ids, accum_ids_)
        if BIG_HACK:
            accum_ids = ut.lmap(condense_accum_ids_stars, accum_ids)
            accum_ids = [('t', ) + x for x in accum_ids]
        ut.dict_set_column(edge_data, 'accum_id', accum_ids)
        return accum_ids

    sources = list(ut.nx_source_nodes(graph))
    print(sources)
    # assert len(sources) == 1, 'expected a unique source'
    source = sources[0]

    graph = graph.subgraph(ut.nx_all_nodes_between(graph, source,
                                                   target)).copy()
    # Remove superfluous data
    ut.nx_delete_edge_attr(
        graph,
        [
            'edge_type',
            'isnwise',
            'nwise_idx',
            # 'parent_colx',
            'ismulti'
        ])

    # Make all '*' edges have distinct local_input_id's.
    # TODO: allow non-distinct suffixes
    count = ord('a')
    for edge in graph.edges(keys=True, data=True):
        dat = edge[3]
        if dat['local_input_id'] == '*':
            dat['local_input_id'] = '*' + chr(count)
            dat['taillabel'] = '*' + chr(count)
            count += 1

    # Augment with dummy super source/sink nodes
    source_input = 'source_input'
    target_output = 'target_output'
    graph.add_edge(source_input, source, local_input_id='s', taillabel='1')
    graph.add_edge(target, target_output, local_input_id='t', taillabel='1')

    # Find all paths from the table to the source.
    paths_to_source = ut.all_multi_paths(graph,
                                         source_input,
                                         target_output,
                                         data=True)

    # Build expanded input graph
    # The inputs to this table can be derived from this graph.
    # The output is a new expanded input graph.
    exi_graph = nx.DiGraph()
    for path in paths_to_source:
        # Accumlate unique identifiers along the reversed path
        edge_list = ut.reverse_path_edges(path)
        accumulate_input_ids(edge_list)

        # A node's output(?) on this path determines its expanded branch id
        exi_nodes = [
            ExiNode(v, BranchId(d['accum_id'], k, d.get('parent_colx', -1)))
            for u, v, k, d in edge_list[:-1]
        ]
        exi_node_to_label = {
            node: node[0] + '[' + ','.join([str(x) for x in node[1]]) + ']'
            for node in exi_nodes
        }
        exi_graph.add_nodes_from(exi_nodes)
        nx.set_node_attributes(exi_graph,
                               name='label',
                               values=exi_node_to_label)

        # Undo any accumulation ordering and remove dummy nodes
        old_edges = ut.reverse_path_edges(edge_list[1:-1])
        new_edges = ut.reverse_path_edges(list(ut.itertwo(exi_nodes)))
        for new_edge, old_edge in zip(new_edges, old_edges):
            u2, v2 = new_edge[:2]
            d = old_edge[3]
            taillabel = d['taillabel']
            parent_colx = d.get('parent_colx', -1)
            if not exi_graph.has_edge(u2, v2):
                exi_graph.add_edge(u2,
                                   v2,
                                   taillabel=taillabel,
                                   parent_colx=parent_colx)

    sink_nodes = list(ut.nx_sink_nodes(exi_graph))
    source_nodes = list(ut.nx_source_nodes(exi_graph))
    assert len(sink_nodes) == 1, 'expected a unique sink'
    sink_node = sink_nodes[0]

    # First identify if a node is root_specifiable
    node_dict = ut.nx_node_dict(exi_graph)
    for node in exi_graph.nodes():
        root_specifiable = False
        # for edge in exi_graph.in_edges(node, keys=True):
        for edge in exi_graph.in_edges(node):
            # key = edge[-1]
            # assert key == 0, 'multi di graph is necessary'
            edata = exi_graph.get_edge_data(*edge)
            if edata.get('taillabel').startswith('*'):
                if node != sink_node:
                    root_specifiable = True
        if exi_graph.in_degree(node) == 0:
            root_specifiable = True
        node_dict[node]['root_specifiable'] = root_specifiable

    # Need to specify any combo of red nodes such that
    # 1) for each path from a (leaf) to the (root) there is exactly one red
    # node along that path.  This garentees that all inputs are gievn.
    path_list = ut.flatten([
        nx.all_simple_paths(exi_graph, source_node, sink_node)
        for source_node in source_nodes
    ])
    rootmost_nodes = set([])
    for path in path_list:
        flags = [node_dict[node]['root_specifiable'] for node in path]
        valid_nodes = ut.compress(path, flags)
        rootmost_nodes.add(valid_nodes[-1])
    # Rootmost nodes are the ones specifiable by default when computing the
    # normal property.
    for node in rootmost_nodes:
        node_dict[node]['rootmost'] = True

    # We actually need to hack away any root-most nodes that have another
    # rootmost node as the parent.  Otherwise, this would cause constraints in
    # what the user could specify as valid input combinations.
    # ie: specify a vocab and an index, but the index depends on the vocab.
    # this forces the user to specify the vocab that was the parent of the index
    # the user should either just specify the index and have the vocab inferred
    # or for now, we just dont allow this to happen.
    nx.get_node_attributes(exi_graph, 'rootmost')

    recolor_exi_graph(exi_graph, rootmost_nodes)
    return exi_graph
Beispiel #7
0
 def continue_if(G, child, edge):
     node_dict = ut.nx_node_dict(G)
     return not node_dict[child].get('root_specifiable')
Beispiel #8
0
 def yield_if(G, child, edge):
     node_dict = ut.nx_node_dict(G)
     return node_dict[child].get('root_specifiable')
Beispiel #9
0
def demo2():
    """
    CommandLine:
        python -m wbia.algo.graph.demo demo2 --viz
        python -m wbia.algo.graph.demo demo2

    Example:
        >>> # DISABLE_DOCTEST
        >>> from wbia.algo.graph.demo import *  # NOQA
        >>> result = demo2()
        >>> print(result)
    """
    import wbia.plottool as pt

    from wbia.scripts.thesis import TMP_RC
    import matplotlib as mpl

    mpl.rcParams.update(TMP_RC)

    # ---- Synthetic data params
    params = {
        'redun.pos': 2,
        'redun.neg': 2,
    }
    # oracle_accuracy = .98
    # oracle_accuracy = .90
    # oracle_accuracy = (.8, 1.0)
    oracle_accuracy = (0.85, 1.0)
    # oracle_accuracy = 1.0

    # --- draw params

    VISUALIZE = ut.get_argflag('--viz')
    # QUIT_OR_EMEBED = 'embed'
    QUIT_OR_EMEBED = 'quit'
    TARGET_REVIEW = ut.get_argval('--target', type_=int, default=None)
    START = ut.get_argval('--start', type_=int, default=None)
    END = ut.get_argval('--end', type_=int, default=None)

    # ------------------

    # rng = np.random.RandomState(42)

    # infr = demodata_infr(num_pccs=4, size=3, size_std=1, p_incon=0)
    # infr = demodata_infr(num_pccs=6, size=7, size_std=1, p_incon=0)
    # infr = demodata_infr(num_pccs=3, size=5, size_std=.2, p_incon=0)
    infr = demodata_infr(pcc_sizes=[5, 2, 4])
    infr.verbose = 100
    # apply_dummy_viewpoints(infr)
    # infr.ensure_cliques()
    infr.ensure_cliques()
    infr.ensure_full()
    # infr.apply_edge_truth()
    # Dummy scoring

    infr.init_simulation(oracle_accuracy=oracle_accuracy, name='demo2')

    # infr_gt = infr.copy()

    dpath = ut.ensuredir(ut.truepath('~/Desktop/demo'))
    ut.remove_files_in_dir(dpath)

    fig_counter = it.count(0)

    def show_graph(infr, title, final=False, selected_edges=None):
        if not VISUALIZE:
            return
        # TODO: rich colored text?
        latest = '\n'.join(infr.latest_logs())
        showkw = dict(
            # fontsize=infr.graph.graph['fontsize'],
            # fontname=infr.graph.graph['fontname'],
            show_unreviewed_edges=True,
            show_inferred_same=False,
            show_inferred_diff=False,
            outof=(len(infr.aids)),
            # show_inferred_same=True,
            # show_inferred_diff=True,
            selected_edges=selected_edges,
            show_labels=True,
            simple_labels=True,
            # show_recent_review=not final,
            show_recent_review=False,
            # splines=infr.graph.graph['splines'],
            reposition=False,
            # with_colorbar=True
        )
        verbose = infr.verbose
        infr.verbose = 0
        infr_ = infr.copy()
        infr_ = infr
        infr_.verbose = verbose
        infr_.show(pickable=True, verbose=0, **showkw)
        infr.verbose = verbose
        # logger.info('status ' + ut.repr4(infr_.status()))
        # infr.show(**showkw)
        ax = pt.gca()
        pt.set_title(title, fontsize=20)
        fig = pt.gcf()
        fontsize = 22
        if True:
            # postprocess xlabel
            lines = []
            for line in latest.split('\n'):
                if False and line.startswith('ORACLE ERROR'):
                    lines += ['ORACLE ERROR']
                else:
                    lines += [line]
            latest = '\n'.join(lines)
            if len(lines) > 10:
                fontsize = 16
            if len(lines) > 12:
                fontsize = 14
            if len(lines) > 14:
                fontsize = 12
            if len(lines) > 18:
                fontsize = 10

            if len(lines) > 23:
                fontsize = 8

        if True:
            pt.adjust_subplots(top=0.95, left=0, right=1, bottom=0.45, fig=fig)
            ax.set_xlabel('\n' + latest)
            xlabel = ax.get_xaxis().get_label()
            xlabel.set_horizontalalignment('left')
            # xlabel.set_x(.025)
            xlabel.set_x(-0.6)
            # xlabel.set_fontname('CMU Typewriter Text')
            xlabel.set_fontname('Inconsolata')
            xlabel.set_fontsize(fontsize)
        ax.set_aspect('equal')

        # ax.xaxis.label.set_color('red')

        from os.path import join

        fpath = join(dpath, 'demo_{:04d}.png'.format(next(fig_counter)))
        fig.savefig(
            fpath,
            dpi=300,
            # transparent=True,
            edgecolor='none',
        )

        # pt.save_figure(dpath=dpath, dpi=300)
        infr.latest_logs()

    if VISUALIZE:
        infr.update_visual_attrs(groupby='name_label')
        infr.set_node_attrs('pin', 'true')
        node_dict = ut.nx_node_dict(infr.graph)
        logger.info(ut.repr4(node_dict[1]))

    if VISUALIZE:
        infr.latest_logs()
        # Pin Nodes into the target groundtruth position
        show_graph(infr, 'target-gt')

    logger.info(ut.repr4(infr.status()))
    infr.clear_feedback()
    infr.clear_name_labels()
    infr.clear_edges()
    logger.info(ut.repr4(infr.status()))
    infr.latest_logs()

    if VISUALIZE:
        infr.update_visual_attrs()

    infr.prioritize('prob_match')
    if VISUALIZE or TARGET_REVIEW is None or TARGET_REVIEW == 0:
        show_graph(infr, 'initial state')

    def on_new_candidate_edges(infr, edges):
        # hack updateing visual attrs as a callback
        infr.update_visual_attrs()

    infr.on_new_candidate_edges = on_new_candidate_edges

    infr.params.update(**params)
    infr.refresh_candidate_edges()

    VIZ_ALL = VISUALIZE and TARGET_REVIEW is None and START is None
    logger.info('VIZ_ALL = %r' % (VIZ_ALL, ))

    if VIZ_ALL or TARGET_REVIEW == 0:
        show_graph(infr, 'find-candidates')

    # _iter2 = enumerate(infr.generate_reviews(**params))
    # _iter2 = list(_iter2)
    # assert len(_iter2) > 0

    # prog = ut.ProgIter(_iter2, label='demo2', bs=False, adjust=False,
    #                    enabled=False)
    count = 1
    first = 1
    for edge, priority in infr._generate_reviews(data=True):
        msg = 'review #%d, priority=%.3f' % (count, priority)
        logger.info('\n----------')
        infr.print('pop edge {} with priority={:.3f}'.format(edge, priority))
        # logger.info('remaining_reviews = %r' % (infr.remaining_reviews()),)
        # Make the next review

        if START is not None:
            VIZ_ALL = count >= START

        if END is not None and count >= END:
            break

        infr.print(msg)
        if ut.allsame(infr.pos_graph.node_labels(*edge)) and first:
            # Have oracle make a mistake early
            feedback = infr.request_oracle_review(edge, accuracy=0)
            first -= 1
        else:
            feedback = infr.request_oracle_review(edge)

        AT_TARGET = TARGET_REVIEW is not None and count >= TARGET_REVIEW - 1

        SHOW_CANDIATE_POP = True
        if SHOW_CANDIATE_POP and (VIZ_ALL or AT_TARGET):
            # import utool
            # utool.embed()
            infr.print(
                ut.repr2(infr.task_probs['match_state'][edge],
                         precision=4,
                         si=True))
            infr.print('len(queue) = %r' % (len(infr.queue)))
            # Show edge selection
            infr.print('Oracle will predict: ' + feedback['evidence_decision'])
            show_graph(infr, 'pre' + msg, selected_edges=[edge])

        if count == TARGET_REVIEW:
            infr.EMBEDME = QUIT_OR_EMEBED == 'embed'
        infr.add_feedback(edge, **feedback)
        infr.print('len(queue) = %r' % (len(infr.queue)))
        # infr.apply_nondynamic_update()
        # Show the result
        if VIZ_ALL or AT_TARGET:
            show_graph(infr, msg)
            # import sys
            # sys.exit(1)
        if count == TARGET_REVIEW:
            break
        count += 1

    infr.print('status = ' + ut.repr4(infr.status(extended=False)))
    show_graph(infr, 'post-review (#reviews={})'.format(count), final=True)

    # ROUND 2 FIGHT
    # if TARGET_REVIEW is None and round2_params is not None:
    #     # HACK TO GET NEW THINGS IN QUEUE
    #     infr.params = round2_params

    #     _iter2 = enumerate(infr.generate_reviews(**params))
    #     prog = ut.ProgIter(_iter2, label='round2', bs=False, adjust=False,
    #                        enabled=False)
    #     for count, (aid1, aid2) in prog:
    #         msg = 'reviewII #%d' % (count)
    #         logger.info('\n----------')
    #         logger.info(msg)
    #         logger.info('remaining_reviews = %r' % (infr.remaining_reviews()),)
    #         # Make the next review evidence_decision
    #         feedback = infr.request_oracle_review(edge)
    #         if count == TARGET_REVIEW:
    #             infr.EMBEDME = QUIT_OR_EMEBED == 'embed'
    #         infr.add_feedback(edge, **feedback)
    #         # Show the result
    #         if PRESHOW or TARGET_REVIEW is None or count >= TARGET_REVIEW - 1:
    #             show_graph(infr, msg)
    #         if count == TARGET_REVIEW:
    #             break

    #     show_graph(infr, 'post-re-review', final=True)

    if not getattr(infr, 'EMBEDME', False):
        if ut.get_computer_name().lower() in ['hyrule', 'ooo']:
            pt.all_figures_tile(monitor_num=0, percent_w=0.5)
        else:
            pt.all_figures_tile()
        ut.show_if_requested()