graphid.core package

Submodules

Module contents

Regenerate Input Command mkinit graphid.core –lazy_loader_typed

class graphid.core.AltConstructors[source]

Bases: object

_graph_cls

alias of NiceGraph

classmethod from_netx(G, verbose=False, infer=True)[source]

Creates an AnnotInference object from a networkx graph

classmethod from_pairs(aid_pairs, attrs=None, verbose=False)[source]
status(extended=False)[source]

Returns information about the state of the graph.

Parameters:

extended (bool) – if True, adds in extra information that requires an O(|E|) amount of computation, otherwise only O(1) stats that are dynamically tracked are returned.

Returns:

a dictionary containing status information. Each of the keys

represents the following information:

nNodes: number of nodes in the graph nEdges: number of edges in the graph nCCs: number of positive connected components nPostvEdges: number of edges labeled as positive nNegtvEdges: number of edges labeled as negative nIncmpEdges: number of edges labeled as incomparable nUnrevEdges: number of edges labeled as unreviewed nPosRedunCCs: the number of PCCs which are currently

k-positive-redundant, i.e. we are confident those PCCs are the same individual.

nNegRedunPairs: the number of PCCs pairs which are

currently k-negative-redundant, i.e. we are confident those PCCs are different individuals.

nInconsistentCCs: the number of inconsistent PCCs that need

to be fixed, i.e. the number of PCCs with an internal negative edges.

If extended is True, then the following keys are also present

nNegEdgesWithin: number of negatives edges inside PCCs nNegEdgesBetween: number of negative edges between PCCs nIncompEdgesWithin: number of incomparable edges inside PCCs nIncompEdgesBetween: number of incomparable edges between PCCs nUnrevEdgesWithin: number of unreviewed edges inside PCCs nUrevEdgesBetween: number of unreviewed edges between PCCs

Return type:

dict

Example

>>> from graphid import demo
>>> infr = demo.demodata_infr(num_pccs=5, p_incon=0.5, pcc_size=10)
>>> print(ub.urepr(infr.status(extended=True)))
{
    'nNodes': 50,
    'nEdges': 93,
    'nCCs': 5,
    'nPostvEdges': 66,
    'nNegtvEdges': 10,
    'nIncmpEdges': 2,
    'nUnrevEdges': 15,
    'nPosRedunCCs': 1,
    'nNegRedunPairs': 2,
    'nInconsistentCCs': 3,
    'nNegEdgesWithin': 4,
    'nNegEdgesBetween': 6,
    'nIncompEdgesWithin': 0,
    'nIncompEdgesBetween': 2,
    'nUnrevEdgesWithin': 15,
    'nUrevEdgesBetween': 0,
}
class graphid.core.AnnotInference(aids=[], nids=None, autoinit=True, verbose=False)[source]

Bases: NiceRepr, AltConstructors, MiscHelpers, Feedback, NameRelabel, Consistency, NonDynamicUpdate, Recovery, DynamicUpdate, Redundancy, Priority, AssertInvariants, DummyEdges, Convenience, AttrAccess, SimulationHelpers, InfrReviewers, InfrLoops, InfrCallbacks, InfrCandidates, GraphVisualization

class for maintaining state of an identification

CommandLine

python -m graphid.core.annot_inference AnnotInference
python -m graphid.core.annot_inference AnnotInference --show

Example

>>> from graphid.core import AnnotInference
>>> import pytest
>>> infr = AnnotInference()
>>> print('infr = {}'.format(infr))
infr = <AnnotInference(nNodes=0, nEdges=0, nCCs=0)>
>>> infr.add_aids(list(range(1, 6)))
>>> print('infr = {}'.format(infr))
infr = <AnnotInference(nNodes=5, nEdges=0, nCCs=5)>
>>> # Add some feedback
>>> infr.params['allow_unseen_nodes'] = False
>>> infr.add_feedback((1, 2), POSTV)
>>> infr.add_feedback((1, 3), INCMP)
>>> infr.add_feedback((1, 4), NEGTV)
>>> with pytest.raises(ValueError):
>>>     infr.add_feedback((1, 10), NEGTV)
>>> with pytest.raises(ValueError):
>>>     infr.add_feedback((11, 12), NEGTV)
>>> print('infr = {}'.format(infr))
infr = <AnnotInference(nNodes=5, nEdges=3, nCCs=4)>
>>> # xdoctest: +REQUIRES(--show)
>>> infr.show_graph()
>>> util.show_if_requested()
copy()[source]
set_config(config, **kw)[source]
subgraph(aids)[source]

Makes a new inference object that is a subset of the original.

Note, this is not robust, be careful. The subgraph should be treated as read only. Do not commit any reviews made from here.

subparams(prefix)[source]

Returns dict of params prefixed with <prefix>. The returned dict does not contain the prefix

Example

>>> infr = AnnotInference()
>>> result = ub.urepr(infr.subparams('refresh'), nl=0, precision=1, sort=1)
>>> print(result)
{'method': 'binomial', 'patience': 72, 'thresh': 0.1, 'window': 20}
class graphid.core.AssertInvariants[source]

Bases: object

assert_consistency_invariant(msg='')[source]
assert_disjoint_invariant(msg='')[source]
assert_edge(edge)[source]
assert_invariants(msg='')[source]
assert_neg_metagraph()[source]

Checks that the negative metgraph is correctly book-kept.

assert_recovery_invariant(msg='')[source]
assert_union_invariant(msg='')[source]
class graphid.core.AttrAccess[source]

Bases: object

Contains non-core helper functions

_get_edges_where(key, op, val, edges=None, default=NoParam, on_missing=None)[source]
edges(data=False)[source]
gen_edge_attrs(key, edges=None, default=NoParam, on_missing=None)[source]

maybe change to gen edge items

gen_edge_values(key, edges=None, default=NoParam, on_missing='error', on_keyerr='default')[source]
gen_node_attrs(key, nodes=None, default=NoParam)[source]
gen_node_values(key, nodes, default=NoParam)[source]
get_annot_attrs(key, aids)[source]

Wrapper around get_node_attrs specific to annotation nodes

get_edge_attr(edge, key, default=NoParam, on_missing='error')[source]

single edge getter helper

get_edge_attrs(key, edges=None, default=NoParam, on_missing=None)[source]

Networkx edge getter helper

get_edge_data(edge)[source]
get_edge_dataframe(edges=None, all=False)[source]
get_edge_df_text(edges=None, highlight=True)[source]
get_edges_where_eq(key, val, edges=None, default=NoParam, on_missing=None)[source]
get_edges_where_ne(key, val, edges=None, default=NoParam, on_missing=None)[source]
get_node_attrs(key, nodes=None, default=NoParam)[source]

Networkx node getter helper

get_nonvisual_edge_data(edge, on_missing='filter')[source]
has_edge(edge)[source]
set_edge_attr(edge, attr)[source]

single edge setter helper

set_edge_attrs(key, edge_to_prop)[source]

Networkx edge setter helper

set_node_attrs(key, node_to_prop)[source]

Networkx node setter helper

class graphid.core.CONFIDENCE[source]

Bases: _Common

ABSOLUTELY_SURE = 4
class CODE

Bases: object

ABSOLUTELY_SURE = 'absolutely_sure'
GUESSING = 'guessing'
NOT_SURE = 'not_sure'
PRETTY_SURE = 'pretty_sure'
UNKNOWN = 'unspecified'
CODE_TO_INT = {'absolutely_sure': 4, 'guessing': 1, 'not_sure': 2, 'pretty_sure': 3, 'unspecified': None}
CODE_TO_NICE = {'absolutely_sure': 'Doubtless', 'guessing': 'Guessing', 'not_sure': 'Unsure', 'pretty_sure': 'Sure', 'unspecified': 'Unspecified'}
GUESSING = 1
INT_TO_CODE = {1: 'guessing', 2: 'not_sure', 3: 'pretty_sure', 4: 'absolutely_sure', None: 'unspecified'}
INT_TO_NICE = {1: 'Guessing', 2: 'Unsure', 3: 'Sure', 4: 'Doubtless', None: 'Unspecified'}
class NICE

Bases: object

ABSOLUTELY_SURE = 'Doubtless'
GUESSING = 'Guessing'
NOT_SURE = 'Unsure'
PRETTY_SURE = 'Sure'
UNKNOWN = 'Unspecified'
NICE_TO_CODE = {'Doubtless': 'absolutely_sure', 'Guessing': 'guessing', 'Sure': 'pretty_sure', 'Unspecified': 'unspecified', 'Unsure': 'not_sure'}
NICE_TO_INT = {'Doubtless': 4, 'Guessing': 1, 'Sure': 3, 'Unspecified': None, 'Unsure': 2}
NOT_SURE = 2
PRETTY_SURE = 3
UNKNOWN = None
class graphid.core.Consistency[source]

Bases: object

consistent_components(graph=None)[source]

Generates consistent PCCs. These PCCs contain no internal negative edges.

Yields:

cc – set: nodes within the PCC

inconsistent_components(graph=None)[source]

Generates inconsistent PCCs. These PCCs contain internal negative edges indicating an error exists.

is_consistent(cc)[source]

Determines if a PCC contains inconsistencies

Parameters:

cc (set) – nodes in a PCC

Returns:

bool: returns True unless cc contains any negative edges

Return type:

flag

Example

>>> from graphid import demo
>>> infr = demo.demodata_infr(num_pccs=1, p_incon=1)
>>> assert not infr.is_consistent(next(infr.positive_components()))
>>> infr = demo.demodata_infr(num_pccs=1, p_incon=0)
>>> assert infr.is_consistent(next(infr.positive_components()))
positive_components(graph=None)[source]

Generates the positive connected compoments (PCCs) in the graph These will contain both consistent and inconsinstent PCCs.

Yields:

cc – set: nodes within the PCC

class graphid.core.Convenience[source]

Bases: object

static e_(u, v)[source]
edge_tag_hist()[source]
property incomp_graph
match_state_df(index)[source]

Returns the current matching state of a list of edges.

PERHAPS WE SHOULD DEPRICATE THIS FUNCTION?

Note

This does NOT use the IBEIS database state, where as the original version of this function did.

CommandLine

python -m graphid.core.mixin_helpers Convenience.match_state_df

Example

>>> from graphid import demo
>>> infr = demo.demodata_infr(num_pccs=2, p_incomp=.8, size=4)
>>> index = list(infr.edges())
>>> print(infr.match_state_df(index))
           NEGTV  POSTV  INCMP
aid1 aid2
1    3     False  False   True
     4     False  False   True
     2     False   True  False
2    3     False  False   True
     4     False  False   True
3    4     False   True  False
     5     False  False   True
5    8     False  False   True
     7     False  False   True
     6     False  False   True
6    8     False  False   True
     7     False  False   True
7    8     False  False   True
property neg_graph
node_tag_hist()[source]
pair_connection_info(aid1, aid2)[source]

Helps debugging when ibs.nids has info that annotmatch/staging do not Note: the relevant ibs parts were removed. Perhaps this is not useful now or should be moved to the ibeis plugin?

Example

>>> from graphid import demo
>>> infr = demo.demodata_infr(num_pccs=3, size=4)
>>> aid1, aid2 = 1, 2
>>> print(infr.pair_connection_info(aid1, aid2))
property pos_graph
print_graph_connections(label='orig_name_label')[source]

label = ‘orig_name_label’

print_graph_info()[source]
print_within_connection_info(edge=None, cc=None, aid=None, nid=None)[source]
property unknown_graph
property unreviewed_graph
class graphid.core.DummyEdges[source]

Bases: object

ensure_cliques(label='name_label', meta_decision=None)[source]

Force each name label to be a clique.

Parameters:
  • label (str) – node attribute to use as the group id to form the cliques.

  • meta_decision (str) – if specified adds clique edges as feedback items with this decision. Otherwise the edges are only explicitly added to the graph.

Parameters:
  • label (str) – defaults to ‘name_label’

  • meta_decision (str) – if specified, the feedback edges added are added this meta decision and with the user_id=algo:clique.

CommandLine

python -m graphid.core.mixin_helpers ensure_cliques

Example

>>> from graphid import demo
>>> label = 'name_label'
>>> infr = demo.demodata_infr(num_pccs=3, size=5)
>>> print(ub.urepr(infr.status()))
>>> assert infr.status()['nEdges'] < 33
>>> infr.ensure_cliques()
>>> print(ub.urepr(infr.status()))
>>> assert infr.status()['nEdges'] == 31
>>> assert infr.status()['nUnrevEdges'] == 12
>>> assert len(list(infr.find_clique_edges(label))) > 0
>>> infr.ensure_cliques(meta_decision=SAME)
>>> assert infr.status()['nUnrevEdges'] == 0
>>> assert len(list(infr.find_clique_edges(label))) == 0
ensure_full()[source]

Explicitly places all edges, but does not make any feedback items

ensure_mst(label='name_label', meta_decision='same')[source]

Ensures that all names are names are connected.

Parameters:
  • label (str) – node attribute to use as the group id to form the mst.

  • meta_decision (str) – if specified adds clique edges as feedback items with this decision. Otherwise the edges are only explicitly added to the graph. This makes feedback items with user_id=algo:mst and with a confidence of guessing.

Example

>>> from graphid import demo
>>> infr = demo.demodata_infr(num_pccs=3, size=4)
>>> assert infr.status()['nCCs'] == 3
>>> infr.clear_edges()
>>> assert infr.status()['nCCs'] == 12
>>> infr.ensure_mst()
>>> assert infr.status()['nCCs'] == 3
find_clique_edges(label='name_label')[source]

Augmenting edges that would complete each the specified cliques. (based on the group inferred from label)

Parameters:

label (str) – node attribute to use as the group id to form the cliques.

find_connecting_edges()[source]

Searches for a small set of edges, which if reviewed as positive would ensure that each PCC is k-connected. Note that in somes cases this is not possible

find_mst_edges(label='name_label')[source]

Returns edges to augment existing PCCs (by label) in order to ensure they are connected with positive edges.

Example

>>> # DISABLE_DOCTEST
>>> from graphid.core.mixin_helpers import *  # NOQA
>>> import ibeis
>>> ibs = ibeis.opendb(defaultdb='PZ_MTEST')
>>> infr = ibeis.AnnotInference(ibs, 'all', autoinit=True)
>>> label = 'orig_name_label'
>>> label = 'name_label'
>>> infr.find_mst_edges()
>>> infr.ensure_mst()
class graphid.core.DynamicUpdate[source]

Bases: object

# 12 total possible states

# details of these states. POSITIVE, WITHIN, CONSISTENT

  • pos-within never changes PCC status

  • never introduces inconsistency

  • might add pos-redun

POSITIVE, WITHIN, INCONSISTENT
  • pos-within never changes PCC status

  • might fix inconsistent edge

POSITIVE, BETWEEN, BOTH_CONSISTENT
  • pos-between edge always does merge

POSITIVE, BETWEEN, ANY_INCONSISTENT
  • pos-between edge always does merge

  • pos-between never fixes inconsistency

NEGATIVE, WITHIN, CONSISTENT
  • might split PCC, results will be consistent

  • might causes an inconsistency

NEGATIVE, WITHIN, INCONSISTENT
  • might split PCC, results may be inconsistent

NEGATIVE, BETWEEN, BOTH_CONSISTENT
  • might add neg-redun

NEGATIVE, BETWEEN, ANY_INCONSISTENT
  • might add to incon-neg-external

  • neg-redun not tracked for incon.

UNINFERABLE, WITHIN, CONSISTENT
  • might remove pos-redun

  • might split PCC, results will be consistent

UNINFERABLE, WITHIN, INCONSISTENT
  • might split PCC, results may be inconsistent

UNINFERABLE, BETWEEN, BOTH_CONSISTENT
  • might remove neg-redun

UNINFERABLE, BETWEEN, ANY_INCONSISTENT
  • might remove incon-neg-external

_add_review_edge(edge, decision)[source]

Adds an edge to the appropriate data structure

_add_review_edges_from(edges, decision='UNREV')[source]
_get_current_decision(edge)[source]

Find if any data structure has the edge

_negative_decision(edge)[source]

Logic for a dynamic negative decision. A negative decision is evidence that two annots should not be in the same PCC

_positive_decision(edge)[source]

Logic for a dynamic positive decision. A positive decision is evidence that two annots should be in the same PCC

Note, this could be an incomparable edge, but with a meta_decision of same.

_uninferable_decision(edge, decision)[source]

Logic for a dynamic uninferable negative decision An uninferrable decision does not provide any evidence about PCC status and is either:

incomparable, unreviewed, or unknown

_update_neg_metagraph(decision, prev_decision, nid1, nid2, merge_nid=None, split_nids=None)[source]

Update the negative metagraph based a new review

Todo

we can likely consolidate lots of neg_redun_metagraph functionality into this function. Just check when the weights are above or under the threshold and update accordingly.

ensure_edges_from(edges)[source]

Finds edges that don’t exist and adds them as unreviwed edges. Returns new edges that were added.

on_between(edge, decision, prev_decision, nid1, nid2, merge_nid=None)[source]

Callback when a review is made between two PCCs

on_within(edge, decision, prev_decision, nid, split_nids=None)[source]

Callback when a review is made inside a PCC

Parameters:
  • edge – the edge reviewed

  • decision – the new decision

  • prev_decision – the old decision

  • nid – the old nid the edge is inside of

  • split_nids – the tuple of new nids created if this decision splits a PCC

class graphid.core.EVIDENCE_DECISION[source]

Bases: _Common

TODO: change to EVIDENCE_DECISION / VISUAL_DECISION Enumerated types of review codes and texts

Notes

Unreviewed: Not comparared yet. nomatch: Visually comparable and the different match: Visually comparable and the same notcomp: Not comparable means it is actually impossible to determine. unknown: means that it was reviewed, but we just can’t figure it out.

class CODE

Bases: object

INCOMPARABLE = 'INCMP'
NEGATIVE = 'NEGTV'
POSITIVE = 'POSTV'
UNKNOWN = 'UNKWN'
UNREVIEWED = 'UNREV'
CODE_TO_INT = {'INCMP': 2, 'NEGTV': 0, 'POSTV': 1, 'UNKWN': 3, 'UNREV': None}
CODE_TO_NICE = {'INCMP': 'Incomparable', 'NEGTV': 'Negative', 'POSTV': 'Positive', 'UNKWN': 'Unknown', 'UNREV': 'Unreviewed'}
INCOMPARABLE = 2
INT_TO_CODE = {0: 'NEGTV', 1: 'POSTV', 2: 'INCMP', 3: 'UNKWN', None: 'UNREV'}
INT_TO_NICE = {0: 'Negative', 1: 'Positive', 2: 'Incomparable', 3: 'Unknown', None: 'Unreviewed'}
MATCH_CODE = {'INCMP': 2, 'NEGTV': 0, 'POSTV': 1, 'UNKWN': 3, 'UNREV': None}
NEGATIVE = 0
class NICE

Bases: object

INCOMPARABLE = 'Incomparable'
NEGATIVE = 'Negative'
POSITIVE = 'Positive'
UNKNOWN = 'Unknown'
UNREVIEWED = 'Unreviewed'
NICE_TO_CODE = {'Incomparable': 'INCMP', 'Negative': 'NEGTV', 'Positive': 'POSTV', 'Unknown': 'UNKWN', 'Unreviewed': 'UNREV'}
NICE_TO_INT = {'Incomparable': 2, 'Negative': 0, 'Positive': 1, 'Unknown': 3, 'Unreviewed': None}
POSITIVE = 1
UNKNOWN = 3
UNREVIEWED = None
class graphid.core.Feedback[source]

Bases: object

_check_edge(edge)[source]
_print_debug_ccs()[source]
_rectify_feedback(feedback)[source]
_rectify_feedback_item(vals)[source]

uses most recently use strategy

add_feedback(edge, evidence_decision=None, tags=None, user_id=None, meta_decision=None, confidence=None, timestamp_c1=None, timestamp_c2=None, timestamp_s1=None, timestamp=None, verbose=None, priority=None)[source]

Primary method for adding feedback and review edges to the graph.

Parameters:
  • edge (tuple) – an undirected edge represented as a pair of aids

  • evidence_decision (str) – decision made based on visual evidence between the two photos. Can be POSTV, NEGTV, INCMP, or UNKWN. Note: POSTV etc… are the variables not the strings.

  • tags (list of str) – additional information to specify

  • user_id (str) – who is doing this review. This can identify a human or algorithm reviewer (e.g. ‘user:joncrall’ or ‘algo:vamp’).

  • meta_decision (str) – decision made based on external knowledge. Perhaps the photographer knows that two animals are the same because all photos are of the same animal. This constrains the identity problem, but does not impact the computer vision learning algorithms, which aren’t given the info needed to make this sort of decision.

  • confidence (str) – how sure is the user of this decision.

  • timestamp_c1 (int) – Time that the review client started

  • timestamp_c2 (int) – Time that the review client ended

  • timestamp_s1 (int) – Time that the review server started

  • timestamp (int) – Time that the review server ended

  • verbose (bool) – verbosity

  • priority (float, optional) – the priority assigned to this edge before review. This is only relevant for the termination criterion.

Notes

If infr.params[‘inference.enabled’] is True, then the edge is inserted into the graph and its properties are updated dynamically. Otherwise it is only added to the internal feedback dictionary and the apply_feedback_edges method must be called.

Example

>>> from graphid import demo
>>> infr = demo.demodata_infr(num_pccs=5)
>>> infr.add_feedback((5, 6), POSTV)
>>> infr.add_feedback((5, 6), NEGTV, tags=['photobomb'])
>>> infr.add_feedback((1, 2), INCMP)
>>> print(ub.urepr(infr.internal_feedback, nl=3, sk=1))
>>> assert len(infr.external_feedback) == 0
>>> assert len(infr.internal_feedback) == 2
>>> assert len(infr.internal_feedback[(5, 6)]) == 2
>>> assert len(infr.internal_feedback[(1, 2)]) == 1
add_feedback_from(items, verbose=None, **kwargs)[source]
Parameters:

items (List[Edge]) – each edge is a dictionary with aid1, aid2, evidence_decision, meta_decision, etc..

add_node_feedback(aid, **attrs)[source]
all_feedback()[source]
all_feedback_items()[source]
apply_feedback_edges()[source]

Transforms the feedback dictionaries into nx graph edge attributes. This

clear_edges()[source]

Removes all edges from the graph

clear_feedback(edges=None)[source]

Delete all edges properties related to feedback

clear_name_labels()[source]

Sets all annotation node name labels to be unknown

edge_decision(edge)[source]

Gets a decision on an edge, either explicitly or implicitly

edge_decision_from(edges)[source]

Gets a decision for multiple edges

feedback_data_keys = ['evidence_decision', 'tags', 'user_id', 'meta_decision', 'timestamp_c1', 'timestamp_c2', 'timestamp_s1', 'timestamp', 'confidence']
feedback_keys = ['evidence_decision', 'tags', 'user_id', 'meta_decision', 'timestamp_c1', 'timestamp_c2', 'timestamp_s1', 'timestamp', 'confidence', 'num_reviews', 'review_id']
reset(state='empty')[source]

Removes all edges from graph and resets name labels.

Example

>>> from graphid.core.annot_inference import *  # NOQA
>>> from graphid import demo
>>> infr = demo.demodata_infr(num_pccs=5)
>>> assert len(list(infr.edges())) > 0
>>> infr.reset(state='empty')
>>> assert len(list(infr.edges())) == 0
reset_name_labels()[source]

Resets all annotation node name labels to their initial values

class graphid.core.GraphVisualization[source]

Bases: object

contains plotting related code

property _error_color
_get_cmap()[source]
_get_truth_colors()[source]
debug_edge_repr()[source]
get_colored_edge_weights(graph=None, highlight_reviews=True)[source]
get_colored_weights(weights)[source]
initialize_visual_node_attrs(graph=None)[source]
pin_node_layout()[source]

Ensures a node layout exists and then sets the pin attribute on each node, which tells graphviz not to change node positions. Useful for making before and after pictures.

repr_edge_data(all_edge_data, visual=True)[source]
show(graph=None, use_image=False, update_attrs=True, with_colorbar=False, pnum=(1, 1, 1), zoomable=True, pickable=False, **kwargs)
Parameters:
  • infr (?)

  • graph (None) – (default = None)

  • use_image (bool) – (default = False)

  • update_attrs (bool) – (default = True)

  • with_colorbar (bool) – (default = False)

  • pnum (tuple) – plot number(default = (1, 1, 1))

  • zoomable (bool) – (default = True)

  • pickable (bool) – (de = False)

  • **kwargs – verbose, with_labels, fnum, layout, ax, pos, img_dict, title, layoutkw, framewidth, modify_ax, as_directed, hacknoedge, hacknode, node_labels, arrow_width, fontsize, fontweight, fontname, fontfamilty, fontproperties

Example

>>> # xdoctest: +REQUIRES(module:pygraphviz)
>>> from graphid import demo
>>> infr = demo.demodata_infr(ccs=util.estarmap(
>>>    range, [(1, 6), (6, 10), (10, 13), (13, 15), (15, 16),
>>>            (17, 20)]))
>>> pnum_ = util.PlotNums(nRows=1, nCols=3)
>>> infr.show_graph(show_cand=True, simple_labels=True, pickable=True, fnum=1, pnum=pnum_())
>>> infr.add_feedback((1, 5), INCMP)
>>> infr.add_feedback((14, 18), INCMP)
>>> infr.refresh_candidate_edges()
>>> infr.show_graph(show_cand=True, simple_labels=True, pickable=True, fnum=1, pnum=pnum_())
>>> infr.add_feedback((17, 18), NEGTV)  # add inconsistency
>>> infr.apply_nondynamic_update()
>>> infr.show_graph(show_cand=True, simple_labels=True, pickable=True, fnum=1, pnum=pnum_())
>>> util.show_if_requested()
show_edge(edge, fnum=None, pnum=None, **kwargs)[source]
show_error_case(aids, edge=None, error_edges=None, colorby=None, fnum=1)[source]

Example

show_graph(graph=None, use_image=False, update_attrs=True, with_colorbar=False, pnum=(1, 1, 1), zoomable=True, pickable=False, **kwargs)[source]
Parameters:
  • infr (?)

  • graph (None) – (default = None)

  • use_image (bool) – (default = False)

  • update_attrs (bool) – (default = True)

  • with_colorbar (bool) – (default = False)

  • pnum (tuple) – plot number(default = (1, 1, 1))

  • zoomable (bool) – (default = True)

  • pickable (bool) – (de = False)

  • **kwargs – verbose, with_labels, fnum, layout, ax, pos, img_dict, title, layoutkw, framewidth, modify_ax, as_directed, hacknoedge, hacknode, node_labels, arrow_width, fontsize, fontweight, fontname, fontfamilty, fontproperties

Example

>>> # xdoctest: +REQUIRES(module:pygraphviz)
>>> from graphid import demo
>>> infr = demo.demodata_infr(ccs=util.estarmap(
>>>    range, [(1, 6), (6, 10), (10, 13), (13, 15), (15, 16),
>>>            (17, 20)]))
>>> pnum_ = util.PlotNums(nRows=1, nCols=3)
>>> infr.show_graph(show_cand=True, simple_labels=True, pickable=True, fnum=1, pnum=pnum_())
>>> infr.add_feedback((1, 5), INCMP)
>>> infr.add_feedback((14, 18), INCMP)
>>> infr.refresh_candidate_edges()
>>> infr.show_graph(show_cand=True, simple_labels=True, pickable=True, fnum=1, pnum=pnum_())
>>> infr.add_feedback((17, 18), NEGTV)  # add inconsistency
>>> infr.apply_nondynamic_update()
>>> infr.show_graph(show_cand=True, simple_labels=True, pickable=True, fnum=1, pnum=pnum_())
>>> util.show_if_requested()
simplify_graph(graph=None, copy=True)[source]
update_node_image_attribute(use_image=False, graph=None)[source]
update_node_image_config(**kwargs)[source]
update_visual_attrs(graph=None, show_reviewed_edges=True, show_unreviewed_edges=False, show_inferred_diff=True, show_inferred_same=True, show_recent_review=False, highlight_reviews=True, show_inconsistency=True, wavy=False, simple_labels=False, show_labels=True, reposition=True, use_image=False, edge_overrides=None, node_overrides=None, colorby='name_label', **kwargs)[source]
property visual_edge_attrs

all edge visual attrs

property visual_edge_attrs_appearance

attrs that pertain to edge color and style

property visual_edge_attrs_space

attrs that pertain to edge positioning in a plot

property visual_node_attrs
class graphid.core.InfrCallbacks[source]

Bases: object

Methods relating to callbacks that must be registered with the inference object for it to work properly.

refresh_candidate_edges()[source]

CommandLine

python -m graphid.core.mixin_callbacks InfrCallbacks.refresh_candidate_edges

Example

>>> from graphid import demo
>>> kwargs = dict(num_pccs=40, size=2)
>>> infr = demo.demodata_infr(**kwargs)
>>> infr.refresh_candidate_edges()
set_ranker(ranker)[source]

ranker should be a function that accepts a list of annotation ids and return a list of the top K ranked annotations.

set_verifier(verifier, task='match_state')[source]

verifier should be a function that accepts a list of annotation pairs and produces the 3-state match_state probabilities.

class graphid.core.InfrCandidates[source]

Bases: object

Methods that should be used by callbacks to add new edges to be considered as candidates in the priority queue.

add_candidate_edges(candidate_edges)[source]
ensure_priority_scores(priority_edges)[source]

Ensures that priority attributes are assigned to the edges. This does not change the state of the queue.

Doctest

>>> from graphid import demo
>>> infr = demo.demodata_infr(num_pccs=6, p_incon=.5, size_std=2)
>>> edges = list(infr.edges())
>>> infr.ensure_priority_scores(edges)
ensure_task_probs(edges)[source]

Ensures that probabilities are assigned to the edges. This gaurentees that infr.task_probs contains data for edges. (Currently only the primary task is actually ensured)

CommandLine

python -m graphid.core.mixin_callbacks InfrCandidates.ensure_task_probs

Doctest

>>> from graphid import demo
>>> infr = demo.demodata_infr(num_pccs=6, p_incon=.5, size_std=2)
>>> edges = list(infr.edges())
>>> infr.ensure_task_probs(edges)
>>> assert all([np.isclose(sum(p.values()), 1)
>>>             for p in infr.task_probs['match_state'].values()])
class graphid.core.InfrLoops[source]

Bases: object

Algorithm control flow loops

_inner_priority_gen(use_refresh=False, only_auto=False)[source]

Helper function that implements the general inner priority loop.

Executes reviews until the queue is empty or needs refresh

Parameters:
  • user_refresh (bool) – if True enables the refresh criteria. (set to True in Phase 1)

  • only_auto (bool) – reviews unless the graph is inconsistent. (set to True in Phase 3)

Notes

The caller is responsible for populating the priority queue. This will iterate until the queue is empty or the refresh critieron is triggered.

hardcase_review_gen()[source]

Subiterator for hardcase review

Re-review non-confident edges that vsone did not classify correctly

incon_recovery_gen()[source]

Subiterator for recovery mode of the mainm algorithm

Iterates until the graph is consistent

Note

inconsistency recovery is implicitly handled by the main algorithm, so other phases do not need to call this explicitly. This exists for the case where the only mode we wish to run is inconsistency recovery.

init_refresh()[source]
main_gen(max_loops=None, use_refresh=True)[source]

The main outer loop.

This function is designed as an iterator that will execute the graph algorithm main loop as automatically as possible, but if user input is needed, it will pause and yield the decision it needs help with. Once feedback is given for this item, you can continue the main loop by calling next. StopIteration is raised once the algorithm is complete.

Parameters:
  • max_loops (int) – maximum number of times to run the outer loop, i.e. ranking is run at most this many times.

  • use_refresh (bool) – allow the refresh criterion to stop the algo

Notes

Different phases of the main loop are implemented as subiterators

CommandLine

python -m graphid.core.mixin_loops InfrLoops.main_gen

Example

>>> from graphid.core.mixin_simulation import UserOracle
>>> from graphid import demo
>>> infr = demo.demodata_infr(num_pccs=3, size=5)
>>> infr.params['manual.n_peek'] = 10
>>> infr.params['ranking.ntop'] = 1
>>> infr.oracle = UserOracle(.99, rng=0)
>>> infr.simulation_mode = False
>>> infr.reset()
>>> gen = infr.main_gen()
>>> while True:
>>>     try:
>>>         reviews = next(gen)
>>>         edge, priority, data = reviews[0]
>>>         feedback = infr.request_oracle_review(edge)
>>>         infr.add_feedback(edge, **feedback)
>>>     except StopIteration:
>>>         break
main_loop(max_loops=None, use_refresh=True)[source]

DEPRICATED

use list(infr.main_gen) instead or assert not any(infr.main_gen()) maybe this is fine.

neg_redun_gen()[source]

Subiterator for phase3 of the main algorithm.

Searches for decisions that would commplete negative redundancy

pos_redun_gen()[source]

Subiterator for phase2 of the main algorithm.

Searches for decisions that would commplete positive redundancy

CommandLine

python -m graphid.core.mixin_loops InfrLoops.pos_redun_gen

Example

>>> from graphid.core.mixin_loops import *
>>> from graphid import demo
>>> infr = demo.demodata_infr(num_pccs=3, size=5, pos_redun=1)
>>> gen = infr.pos_redun_gen()
>>> feedback = next(gen)
>>> edge_ = feedback[0][0]
>>> print(edge_)
(1, 5)
ranked_list_gen(use_refresh=True)[source]

Subiterator for phase1 of the main algorithm

Calls the underlying ranking algorithm and prioritizes the results

start_id_review(max_loops=None, use_refresh=None)[source]
class graphid.core.InfrReviewers[source]

Bases: object

_make_review_tuple(edge, priority=None)[source]

Makes tuple to be sent back to the user

accept(feedback)[source]

Called when user has completed feedback from qt or web

continue_review()[source]
emit_manual_review(edge, priority=None)[source]

Emits a signal containing edges that need review. The callback should present them to a user, get feedback, and then call on_accpet.

request_oracle_review(edge, **kw)[source]
skip(edge)[source]
try_auto_review(edge)[source]
class graphid.core.META_DECISION[source]

Bases: _Common

Enumerated types of review codes and texts

Notes

unreviewed: we dont have a meta decision same: we know this is the same animal through non-visual means diff: we know this is the different animal through non-visual means

Example

>>> assert hasattr(META_DECISION, 'CODE')
>>> assert hasattr(META_DECISION, 'NICE')
>>> code1 = META_DECISION.INT_TO_CODE[META_DECISION.NULL]
>>> code2 = META_DECISION.CODE.NULL
>>> assert code1 == code2
>>> nice1 = META_DECISION.INT_TO_NICE[META_DECISION.NULL]
>>> nice2 = META_DECISION.NICE.NULL
>>> assert nice1 == nice2
class CODE

Bases: object

DIFF = 'diff'
NULL = 'null'
SAME = 'same'
CODE_TO_INT = {'diff': 0, 'null': None, 'same': 1}
CODE_TO_NICE = {'diff': 'Different', 'null': 'NULL', 'same': 'Same'}
DIFF = 0
INT_TO_CODE = {0: 'diff', 1: 'same', None: 'null'}
INT_TO_NICE = {0: 'Different', 1: 'Same', None: 'NULL'}
class NICE

Bases: object

DIFF = 'Different'
NULL = 'NULL'
SAME = 'Same'
NICE_TO_CODE = {'Different': 'diff', 'NULL': 'null', 'Same': 'same'}
NICE_TO_INT = {'Different': 0, 'NULL': None, 'Same': 1}
NULL = None
SAME = 1
class graphid.core.MiscHelpers[source]

Bases: object

_rectify_nids(aids, nids)[source]
add_aids(aids, nids=None)[source]

CommandLine

python -m graphid.core.annot_inference MiscHelpers.add_aids

Doctest

>>> aids_ = [1, 2, 3, 4, 5, 6, 7, 9]
>>> infr = AnnotInference(aids=aids_, autoinit=True)
>>> aids = [2, 22, 7, 9, 8]
>>> nids = None
>>> infr.add_aids(aids, nids)
>>> result = infr.aids
>>> print(result)
>>> assert len(infr.graph) == len(infr.aids)
[1, 2, 3, 4, 5, 6, 7, 9, 22, 8]
dump_logs()[source]
initialize_graph(graph=None)[source]

Constructs the internal networkx Graph objects

latest_logs(colored=False)[source]
print(msg, level=1, color=None)[source]
remove_aids(aids)[source]

Remove annotations from the graph.

Returns:

split: indicates which PCCs were split by this action.

Return type:

dict

Note

This may cause unintended splits!

CommandLine

xdoctest -m graphid.core.annot_inference MiscHelpers.remove_aids

Example

>>> from graphid import demo, util
>>> infr = demo.demodata_infr(num_pccs=5, pos_redun=1)
>>> infr.refresh_candidate_edges()
>>> infr.pin_node_layout()
>>> before = infr.copy()
>>> aids = infr.aids[::5]
>>> splits = infr.remove_aids(aids)
>>> assert len(splits['old']) > 0
>>> infr.assert_invariants()
>>> # xdoctest: +REQUIRES(--show)
>>> util.autompl()
>>> after = infr
>>> before.show(fnum=1, pnum=(1, 2, 1), pickable=True)
>>> after.show(fnum=1, pnum=(1, 2, 2), pickable=True)
update_node_attributes(aids=None, nids=None)[source]
class graphid.core.NameRelabel[source]

Bases: object

_next_nid()[source]
_rectified_relabel(cc_subgraphs)[source]

Reuses as many names as possible

_rectify_names(old_names, new_labels)[source]

Finds the best assignment of old names based on the new groups each is assigned to.

old_names = [None, None, None, 1, 2, 3, 3, 4, 4, 4, 5, None] new_labels = [ 1, 2, 2, 3, 4, 5, 5, 6, 3, 3, 7, 7]

node_label(aid)[source]
node_labels(*aids)[source]
relabel_using_reviews(graph=None, rectify=True)[source]

Relabels nodes in graph based on positive connected components

This will change the ‘name_label’ of the nodes to be consistent while preserving any existing names as best as possible. If rectify=False, this will be faster, but the old names may not be preserved and each PCC will be assigned an arbitrary name.

Note

if something messes up you can call infr.reset_labels_to_ibeis() to reset node labels to their original values — this will almost always put the graph in an inconsistent state — but then you can this with rectify=True to fix everything up.

Parameters:
  • graph (nx.Graph, optional) – only edges in graph are relabeled defaults to current graph.

  • rectify (bool, optional) – if True names attempt to remain consistent otherwise there are no restrictions on name labels other than that they are distinct.

Example

>>> from graphid import demo, util
>>> infr = demo.demodata_infr(num_pccs=5, pos_redun=1)
>>> names0 = set(infr.get_node_attrs('name_label').values())
>>> infr.relabel_using_reviews(rectify=True)
>>> names1 = set(infr.get_node_attrs('name_label').values())
>>> assert names0 == names1
>>> # wont change because its the entire graph
>>> #infr.relabel_using_reviews(rectify=False)
>>> #names2 = set(infr.get_node_attrs('name_label').values())
class graphid.core.NonDynamicUpdate[source]

Bases: object

apply_nondynamic_update(graph=None)[source]

Recomputes all dynamic bookkeeping for a graph in any state. This ensures that subsequent dyanmic inference can be applied.

Example

>>> from graphid import demo
>>> num_pccs = 250
>>> kwargs = dict(num_pccs=100, p_incon=.3)
>>> infr = demo.demodata_infr(infer=False, **kwargs)
>>> graph = None
>>> infr.apply_nondynamic_update()
>>> infr.assert_neg_metagraph()
categorize_edges(graph=None, ne_to_edges=None)[source]

Non-dynamically computes the status of each edge in the graph. This is can be used to verify the dynamic computations and update when the dynamic state is lost.

Example

>>> from graphid import demo
>>> num_pccs = 250 if ub.argflag('--profile') else 100
>>> kwargs = dict(num_pccs=100, p_incon=.3)
>>> infr = demo.demodata_infr(infer=False, **kwargs)
>>> graph = None
>>> cat = infr.categorize_edges()
collapsed_meta_edges(graph=None)[source]

Collapse the grah such that each PCC is a node. Get a list of edges within/between each PCC.

class graphid.core.Priority[source]

Bases: object

Handles prioritization of edges for review.

Example

>>> from graphid.core.mixin_priority import *  # NOQA
>>> from graphid import demo
>>> infr = demo.demodata_infr(num_pccs=20)
_generate_reviews(data=False)[source]
_increase_priority(edges, amount=10)[source]
_peek_many(n)[source]

Wraps queue so ordering is determenistic

_pop(*args)[source]

Wraps queue so ordering is determenistic

_push(edge, priority)[source]

Wraps queue so ordering is determenistic

_reinstate_edge_priority(edges)[source]
_remove_edge_priority(edges)[source]
confidently_connected(u, v, thresh=2)[source]

Checks if u and v are conneted by edges above a confidence threshold

confidently_separated(u, v, thresh=2)[source]

Checks if u and v are conneted by edges above a confidence threshold

Example

>>> from graphid.core.mixin_priority import *  # NOQA
>>> from graphid import demo
>>> infr = demo.demodata_infr(ccs=[(1, 2), (3, 4), (5, 6), (7, 8)])
>>> infr.add_feedback((1, 5), NEGTV)
>>> infr.add_feedback((5, 8), NEGTV)
>>> infr.add_feedback((6, 3), NEGTV)
>>> u, v = (1, 4)
>>> thresh = 0
>>> assert not infr.confidently_separated(u, v, thresh)
>>> infr.add_feedback((2, 3), NEGTV)
>>> assert not infr.confidently_separated(u, v, thresh)
generate_reviews(pos_redun=None, neg_redun=None, data=False)[source]

Dynamic generator that yeilds high priority reviews

peek()[source]
peek_many(n)[source]

Peeks at the top n edges in the queue.

Example

>>> # ENABLE_DOCTEST
>>> from graphid.core.mixin_priority import *  # NOQA
>>> from graphid import demo
>>> infr = demo.demodata_infr(num_pccs=7, size=5)
>>> infr.refresh_candidate_edges()
>>> infr.peek_many(50)
pop()[source]

Main interface to the priority queue used by the algorithm loops. Pops the highest priority edge from the queue.

prioritize(metric=None, edges=None, scores=None, force_inconsistent=True, reset=False)[source]

Adds edges to the priority queue.

Note that these edges must already exist in the infr.unreviewed_graph as unreviewed edges. By default the prob_match edge attribute is used to sort edges. If you have registered a verification algorithm, then these scores are computed using infr.ensure_priority_scores(edges). However, you can have all this done for you by simply calling infr.add_candidate_edges(edges) or infr.refresh_candidate_edges().

Example

>>> from graphid.core.mixin_priority import *  # NOQA
>>> from graphid import demo
>>> import ubelt as ub
>>> from graphid.core.state import SAME
>>> infr = demo.demodata_infr(num_pccs=7, size=5)
>>> infr.ensure_cliques(meta_decision=SAME)
>>> # Add a negative edge inside a PCC
>>> ccs = list(infr.positive_components())
>>> edge1 = tuple(list(ccs[0])[0:2])
>>> edge2 = tuple(list(ccs[1])[0:2])
>>> infr.add_feedback(edge1, NEGTV)
>>> infr.add_feedback(edge2, NEGTV)
>>> num_new = infr.prioritize(reset=True)
>>> order = infr._peek_many(np.inf)
>>> scores = util.take_column(order, 1)
>>> assert scores[0] > 10
>>> assert len(scores) == num_new, 'should prioritize two hypotheis edges'
>>> unrev_edges = set(infr.unreviewed_graph.edges())
>>> err_edges = set(ub.flatten(infr.nid_to_errors.values()))
>>> edges = set(list(unrev_edges - err_edges)[0:2])
>>> edges.update(list(err_edges)[0:2])
>>> num_new = infr.prioritize(edges=edges, reset=True)
>>> order2 = infr._peek_many(np.inf)
>>> scores2 = np.array(util.take_column(order2, 1))
>>> assert np.all(scores2[0:2] > 10)
>>> assert np.all(scores2[2:] < 10)
push(edge, priority=None)[source]

Push an edge back onto the queue

reinstate_internal_priority(cc)[source]
remaining_reviews()[source]
remove_internal_priority(cc)[source]
class graphid.core.QUAL[source]

Bases: _Common

class CODE

Bases: object

EXCELLENT = 'excellent'
GOOD = 'good'
JUNK = 'junk'
OK = 'ok'
POOR = 'poor'
UNKNOWN = 'unspecified'
CODE_TO_INT = {'excellent': 5, 'good': 4, 'junk': 1, 'ok': 3, 'poor': 2, 'unspecified': None}
CODE_TO_NICE = {'excellent': 'Excellent', 'good': 'Good', 'junk': 'Junk', 'ok': 'OK', 'poor': 'Poor', 'unspecified': 'Unspecified'}
EXCELLENT = 5
GOOD = 4
INT_TO_CODE = {1: 'junk', 2: 'poor', 3: 'ok', 4: 'good', 5: 'excellent', None: 'unspecified'}
INT_TO_NICE = {1: 'Junk', 2: 'Poor', 3: 'OK', 4: 'Good', 5: 'Excellent', None: 'Unspecified'}
JUNK = 1
class NICE

Bases: object

EXCELLENT = 'Excellent'
GOOD = 'Good'
JUNK = 'Junk'
OK = 'OK'
POOR = 'Poor'
UNKNOWN = 'Unspecified'
NICE_TO_CODE = {'Excellent': 'excellent', 'Good': 'good', 'Junk': 'junk', 'OK': 'ok', 'Poor': 'poor', 'Unspecified': 'unspecified'}
NICE_TO_INT = {'Excellent': 5, 'Good': 4, 'Junk': 1, 'OK': 3, 'Poor': 2, 'Unspecified': None}
OK = 3
POOR = 2
UNKNOWN = None
class graphid.core.Recovery[source]

Bases: object

recovery funcs

_check_inconsistency(nid, cc=None)[source]

Check if a PCC contains an error

_mincut_edge_weights(edges_)[source]
_new_inconsistency(nid)[source]
_purge_error_edges(nid)[source]

Removes all error edges associated with a PCC so they can be recomputed or resolved.

_set_error_edges(nid, new_error_edges)[source]
hypothesis_errors(pos_subgraph, neg_edges)[source]
is_recovering(edge=None)[source]

Checks to see if the graph is inconsinsistent.

Parameters:

edge (None) – If None, then returns True if the graph contains any inconsistency. Otherwise, returns True if the edge is related to an inconsistent component via a positive or negative connection.

Returns:

flag

Return type:

bool

CommandLine

python -m graphid.core.mixin_dynamic Recovery.is_recovering

Doctest

>>> from graphid import demo
>>> infr = demo.demodata_infr(num_pccs=4, size=4, ignore_pair=True)
>>> infr.ensure_cliques(meta_decision=SAME)
>>> a, b, c, d = map(list, infr.positive_components())
>>> assert infr.is_recovering() is False
>>> infr.add_feedback((a[0], a[1]), NEGTV)
>>> assert infr.is_recovering() is True
>>> assert infr.is_recovering((a[2], a[3])) is True
>>> assert infr.is_recovering((a[3], b[0])) is True
>>> assert infr.is_recovering((b[0], b[1])) is False
>>> infr.add_feedback((a[3], b[2]), NEGTV)
>>> assert infr.is_recovering((b[0], b[1])) is True
>>> assert infr.is_recovering((c[0], d[0])) is False
>>> infr.add_feedback((b[2], c[0]), NEGTV)
>>> assert infr.is_recovering((c[0], d[0])) is False
>>> result = ub.urepr({
>>>     'iccs': list(infr.inconsistent_components()),
>>>     'pccs': sorted([cc for cc in infr.positive_components()], key=min),
>>> }, nobr=1, sorted=True, si=True, itemsep='', sep='', nl=1)
>>> print(result)
iccs: [{1,2,3,4}],
pccs: [{1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,16}],
maybe_error_edges()[source]
class graphid.core.Redundancy[source]

Bases: _RedundancyComputers, _RedundancyAugmentation

methods for dynamic redundancy book-keeping

_purge_redun_flags(nid)[source]

Removes positive and negative redundancy from nids and all other PCCs touching nids respectively. Return the external PCC nids.

(TODO: NEG REDUN CAN BE CONSOLIDATED VIA NEG-META-GRAPH)

_set_neg_redun_flags(nid1, other_nids, flags)[source]

Flags or unflags an nid1 as negative redundant with other nids. (TODO: NEG REDUN CAN BE CONSOLIDATED VIA NEG-META-GRAPH)

_set_pos_redun_flag(nid, flag)[source]

Flags or unflags an nid as positive redundant.

filter_edges_flagged_as_redun(edges)[source]

Returns only edges that are not flagged as redundant. Uses bookkeeping structures

Example

>>> from graphid import demo
>>> infr = demo.demodata_infr(num_pccs=1, size=4)
>>> infr.clear_edges()
>>> infr.ensure_cliques()
>>> infr.clear_feedback()
>>> print(ub.urepr(infr.status()))
>>> nonredun_edges = list(infr.filter_edges_flagged_as_redun(
>>>     infr.unreviewed_graph.edges()))
>>> assert len(nonredun_edges) == 6
is_flagged_as_redun(edge)[source]

Tests redundancy against bookkeeping structure against cache

update_extern_neg_redun(nid, may_add=True, may_remove=True, force=False)[source]

Checks if nid is negative redundant to any other cc it has at least one negative review to. (TODO: NEG REDUN CAN BE CONSOLIDATED VIA NEG-META-GRAPH)

update_neg_redun_to(nid1, other_nids, may_add=True, may_remove=True, force=False)[source]

Checks if nid1 is neg redundant to other_nids. Edges are either removed or added to the queue appropriately. (TODO: NEG REDUN CAN BE CONSOLIDATED VIA NEG-META-GRAPH)

update_pos_redun(nid, may_add=True, may_remove=True, force=False)[source]

Checks if a PCC is newly, or no longer positive redundant. Edges are either removed or added to the queue appropriately.

class graphid.core.RefreshCriteria(window=20, patience=72, thresh=0.1, method='binomial')[source]

Bases: object

Determine when to re-query for candidate edges.

Models an upper bound on the probability that any of the next patience reviews will be label-changing (meaningful). Once this probability is below a threshold the criterion triggers. The model is either binomial or poisson. They both work about the same. The binomial is a slightly better model.

Does this by maintaining an estimate of the probability any particular review will be label-chaging using an exponentially weighted moving average. This is the rate parameter / individual event probability.

_prob_none_remain(n_remain_edges=None)[source]
add(meaningful, user_id, decision=None)[source]
ave(method='exp')[source]

Example

>>> from graphid import demo
>>> infr = demo.demodata_infr(num_pccs=40, size=4, size_std=2, ignore_pair=True)
>>> edges = list(infr.ranker.predict_candidate_edges(infr.aids, K=100))
>>> scores = np.array(infr.verifier.predict_edges(edges))
>>> #sortx = util.shuffle(np.arange(len(edges)), rng=321)
>>> sortx = scores.argsort()[::-1]
>>> edges = list(ub.take(edges, sortx))
>>> scores = scores[sortx]
>>> ys = infr.match_state_df(edges)[POSTV].values
>>> y_remainsum = ys[::-1].cumsum()[::-1]
>>> refresh = RefreshCriteria(window=250)
>>> ma1 = []
>>> ma2 = []
>>> reals = []
>>> xdata = []
>>> for count, (edge, y) in enumerate(zip(edges, ys)):
>>>     refresh.add(y, user_id='user:oracle')
>>>     ma1.append(refresh._ewma)
>>>     ma2.append(refresh.pos_frac)
>>>     n_real = y_remainsum[count] / (len(edges) - count)
>>>     reals.append(n_real)
>>>     xdata.append(count + 1)
>>> # xdoctest: +REQUIRES(--show)
>>> from graphid import util
>>> util.qtensure()
>>> util.multi_plot(xdata, [ma1, ma2, reals], marker='',
>>>               label_list=['exp', 'win', 'real'], xlabel='review num',
>>>               ylabel='mu')
../_static/images/fig_graphid_core_RefreshCriteria_ave_002.jpeg
check()[source]
clear()[source]
property pos_frac
pred_num_positives(n_remain_edges)[source]

Uses poisson process to estimate remaining positive reviews.

Multipling mu * n_remain_edges gives a probabilistic upper bound on the number of errors remaning. This only provides a real estimate if reviewing in a random order

Example

>>> # ENABLE_DOCTEST
>>> from graphid.core.refresh import *  # NOQA
>>> from graphid import demo
>>> infr = demo.demodata_infr(num_pccs=50, size=4, size_std=2)
>>> edges = list(infr.ranker.predict_candidate_edges(infr.aids, K=100))
>>> #edges = util.shuffle(sorted(edges), rng=321)
>>> scores = np.array(infr.verifier.predict_edges(edges))
>>> sortx = scores.argsort()[::-1]
>>> edges = list(ub.take(edges, sortx))
>>> scores = scores[sortx]
>>> ys = infr.match_state_df(edges)[POSTV].values
>>> y_remainsum = ys[::-1].cumsum()[::-1]
>>> refresh = RefreshCriteria(window=250)
>>> n_pred_list = []
>>> n_real_list = []
>>> xdata = []
>>> for count, (edge, y) in enumerate(zip(edges, ys)):
>>>     refresh.add(y, user_id='user:oracle')
>>>     n_remain_edges = len(edges) - count
>>>     n_pred = refresh.pred_num_positives(n_remain_edges)
>>>     n_real = y_remainsum[count]
>>>     if count == 2000:
>>>         break
>>>     n_real_list.append(n_real)
>>>     n_pred_list.append(n_pred)
>>>     xdata.append(count + 1)
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.autompl()
>>> import plottool_ibeis as pt
>>> #pt.qtensure()
>>> n_pred_list = n_pred_list[10:]
>>> n_real_list = n_real_list[10:]
>>> xdata = xdata[10:]
>>> pt.multi_plot(xdata, [n_pred_list, n_real_list], marker='',
>>>               label_list=['pred', 'real'], xlabel='review num',
>>>               ylabel='pred remaining merges')
>>> stop_point = xdata[np.where(y_remainsum[10:] == 0)[0][0]]
>>> pt.gca().plot([stop_point, stop_point], [0, int(max(n_pred_list))], 'g-')
prob_any_remain(n_remain_edges=None)[source]
class graphid.core.SimulationHelpers[source]

Bases: object

_dynamic_test_callback(edge, decision, prev_decision, user_id)[source]
_print_previous_loop_statistics(count)[source]
init_simulation(oracle_accuracy=1.0, k_redun=2, enable_autoreview=True, enable_inference=True, classifiers=None, match_state_thresh=None, max_outer_loops=None, name=None)[source]
init_test_mode()[source]
measure_error_edges()[source]
measure_metrics()[source]
class graphid.core.UserOracle(accuracy, rng)[source]

Bases: object

review(edge, truth, infr, accuracy=None)[source]
class graphid.core.VIEW[source]

Bases: _Common

categorical viewpoint using the faces of a Rhombicuboctahedron

References

https://en.wikipedia.org/wiki/Rhombicuboctahedron

B = 7
BL = 6
BR = 8
class CODE

Bases: object

B = 'back'
BL = 'backleft'
BR = 'backright'
D = 'down'
DB = 'downback'
DBL = 'downbackleft'
DBR = 'downbackright'
DF = 'downfront'
DFL = 'downfrontleft'
DFR = 'downfrontright'
DL = 'downleft'
DR = 'downright'
F = 'front'
FL = 'frontleft'
FR = 'frontright'
L = 'left'
R = 'right'
U = 'up'
UB = 'upback'
UBL = 'upbackleft'
UBR = 'upbackright'
UF = 'upfront'
UFL = 'upfrontleft'
UFR = 'upfrontright'
UL = 'upleft'
UNKNOWN = 'unknown'
UR = 'upright'
CODE_TO_INT = {'back': 7, 'backleft': 6, 'backright': 8, 'down': 18, 'downback': 20, 'downbackleft': 25, 'downbackright': 26, 'downfront': 19, 'downfrontleft': 23, 'downfrontright': 24, 'downleft': 21, 'downright': 22, 'front': 3, 'frontleft': 4, 'frontright': 2, 'left': 5, 'right': 1, 'unknown': None, 'up': 9, 'upback': 11, 'upbackleft': 16, 'upbackright': 17, 'upfront': 10, 'upfrontleft': 14, 'upfrontright': 15, 'upleft': 12, 'upright': 13}
CODE_TO_NICE = {'back': 'Back', 'backleft': 'Back-Left', 'backright': 'Back-Right', 'down': 'Down', 'downback': 'Down-Back', 'downbackleft': 'Down-Back-Left', 'downbackright': 'Down-Back-Right', 'downfront': 'Down-Front', 'downfrontleft': 'Down-Front-Left', 'downfrontright': 'Down-Front-Right', 'downleft': 'Down-Left', 'downright': 'Down-Right', 'front': 'Front', 'frontleft': 'Front-Left', 'frontright': 'Front-Right', 'left': 'Left', 'right': 'Right', 'unknown': 'Unknown', 'up': 'Up', 'upback': 'Up-Back', 'upbackleft': 'Up-Back-Left', 'upbackright': 'Up-Back-Right', 'upfront': 'Up-Front', 'upfrontleft': 'Up-Front-Left', 'upfrontright': 'Up-Front-Right', 'upleft': 'Up-Left', 'upright': 'Up-Right'}
D = 18
DB = 20
DBL = 25
DBR = 26
DF = 19
DFL = 23
DFR = 24
DIST = {(1, 1): 0, (1, 10): 2, (1, 11): 2, (1, 12): 3, (1, 13): 1, (1, 14): 3, (1, 15): 1, (1, 16): 3, (1, 17): 1, (1, 18): 2, (1, 19): 2, (1, 2): 1, (1, 20): 2, (1, 21): 3, (1, 22): 1, (1, 23): 3, (1, 24): 1, (1, 25): 3, (1, 26): 1, (1, 3): 2, (1, 4): 3, (1, 5): 4, (1, 6): 3, (1, 7): 2, (1, 8): 1, (1, 9): 2, (1, None): None, (10, 1): 2, (10, 10): 0, (10, 11): 2, (10, 12): 2, (10, 13): 2, (10, 14): 1, (10, 15): 1, (10, 16): 2, (10, 17): 2, (10, 18): 3, (10, 19): 2, (10, 2): 2, (10, 20): 4, (10, 21): 3, (10, 22): 3, (10, 23): 2, (10, 24): 2, (10, 25): 3, (10, 26): 3, (10, 3): 1, (10, 4): 2, (10, 5): 2, (10, 6): 3, (10, 7): 3, (10, 8): 3, (10, 9): 1, (10, None): None, (11, 1): 2, (11, 10): 2, (11, 11): 0, (11, 12): 2, (11, 13): 2, (11, 14): 2, (11, 15): 2, (11, 16): 1, (11, 17): 1, (11, 18): 3, (11, 19): 4, (11, 2): 3, (11, 20): 2, (11, 21): 3, (11, 22): 3, (11, 23): 3, (11, 24): 3, (11, 25): 2, (11, 26): 2, (11, 3): 3, (11, 4): 3, (11, 5): 2, (11, 6): 2, (11, 7): 1, (11, 8): 2, (11, 9): 1, (11, None): None, (12, 1): 3, (12, 10): 2, (12, 11): 2, (12, 12): 0, (12, 13): 2, (12, 14): 1, (12, 15): 2, (12, 16): 1, (12, 17): 2, (12, 18): 3, (12, 19): 3, (12, 2): 3, (12, 20): 3, (12, 21): 2, (12, 22): 4, (12, 23): 2, (12, 24): 3, (12, 25): 2, (12, 26): 3, (12, 3): 2, (12, 4): 2, (12, 5): 1, (12, 6): 2, (12, 7): 2, (12, 8): 3, (12, 9): 1, (12, None): None, (13, 1): 1, (13, 10): 2, (13, 11): 2, (13, 12): 2, (13, 13): 0, (13, 14): 2, (13, 15): 1, (13, 16): 2, (13, 17): 1, (13, 18): 3, (13, 19): 3, (13, 2): 2, (13, 20): 3, (13, 21): 4, (13, 22): 2, (13, 23): 3, (13, 24): 2, (13, 25): 3, (13, 26): 2, (13, 3): 2, (13, 4): 3, (13, 5): 3, (13, 6): 3, (13, 7): 2, (13, 8): 2, (13, 9): 1, (13, None): None, (14, 1): 3, (14, 10): 1, (14, 11): 2, (14, 12): 1, (14, 13): 2, (14, 14): 0, (14, 15): 2, (14, 16): 2, (14, 17): 2, (14, 18): 3, (14, 19): 2, (14, 2): 2, (14, 20): 3, (14, 21): 2, (14, 22): 3, (14, 23): 2, (14, 24): 2, (14, 25): 2, (14, 26): 4, (14, 3): 1, (14, 4): 1, (14, 5): 1, (14, 6): 2, (14, 7): 3, (14, 8): 3, (14, 9): 1, (14, None): None, (15, 1): 1, (15, 10): 1, (15, 11): 2, (15, 12): 2, (15, 13): 1, (15, 14): 2, (15, 15): 0, (15, 16): 2, (15, 17): 2, (15, 18): 3, (15, 19): 2, (15, 2): 1, (15, 20): 3, (15, 21): 3, (15, 22): 2, (15, 23): 2, (15, 24): 2, (15, 25): 4, (15, 26): 2, (15, 3): 1, (15, 4): 2, (15, 5): 3, (15, 6): 3, (15, 7): 3, (15, 8): 2, (15, 9): 1, (15, None): None, (16, 1): 3, (16, 10): 2, (16, 11): 1, (16, 12): 1, (16, 13): 2, (16, 14): 2, (16, 15): 2, (16, 16): 0, (16, 17): 2, (16, 18): 3, (16, 19): 3, (16, 2): 3, (16, 20): 2, (16, 21): 2, (16, 22): 3, (16, 23): 2, (16, 24): 4, (16, 25): 2, (16, 26): 2, (16, 3): 3, (16, 4): 2, (16, 5): 1, (16, 6): 1, (16, 7): 1, (16, 8): 2, (16, 9): 1, (16, None): None, (17, 1): 1, (17, 10): 2, (17, 11): 1, (17, 12): 2, (17, 13): 1, (17, 14): 2, (17, 15): 2, (17, 16): 2, (17, 17): 0, (17, 18): 3, (17, 19): 3, (17, 2): 2, (17, 20): 2, (17, 21): 3, (17, 22): 2, (17, 23): 4, (17, 24): 2, (17, 25): 2, (17, 26): 2, (17, 3): 3, (17, 4): 3, (17, 5): 3, (17, 6): 2, (17, 7): 1, (17, 8): 1, (17, 9): 1, (17, None): None, (18, 1): 2, (18, 10): 3, (18, 11): 3, (18, 12): 3, (18, 13): 3, (18, 14): 3, (18, 15): 3, (18, 16): 3, (18, 17): 3, (18, 18): 0, (18, 19): 1, (18, 2): 2, (18, 20): 1, (18, 21): 1, (18, 22): 1, (18, 23): 1, (18, 24): 1, (18, 25): 1, (18, 26): 1, (18, 3): 2, (18, 4): 2, (18, 5): 2, (18, 6): 2, (18, 7): 2, (18, 8): 2, (18, 9): 4, (18, None): None, (19, 1): 2, (19, 10): 2, (19, 11): 4, (19, 12): 3, (19, 13): 3, (19, 14): 2, (19, 15): 2, (19, 16): 3, (19, 17): 3, (19, 18): 1, (19, 19): 0, (19, 2): 2, (19, 20): 2, (19, 21): 2, (19, 22): 2, (19, 23): 1, (19, 24): 1, (19, 25): 2, (19, 26): 2, (19, 3): 1, (19, 4): 2, (19, 5): 2, (19, 6): 3, (19, 7): 3, (19, 8): 3, (19, 9): 3, (19, None): None, (2, 1): 1, (2, 10): 2, (2, 11): 3, (2, 12): 3, (2, 13): 2, (2, 14): 2, (2, 15): 1, (2, 16): 3, (2, 17): 2, (2, 18): 2, (2, 19): 2, (2, 2): 0, (2, 20): 3, (2, 21): 3, (2, 22): 2, (2, 23): 2, (2, 24): 1, (2, 25): 3, (2, 26): 2, (2, 3): 1, (2, 4): 2, (2, 5): 3, (2, 6): 4, (2, 7): 3, (2, 8): 2, (2, 9): 2, (2, None): None, (20, 1): 2, (20, 10): 4, (20, 11): 2, (20, 12): 3, (20, 13): 3, (20, 14): 3, (20, 15): 3, (20, 16): 2, (20, 17): 2, (20, 18): 1, (20, 19): 2, (20, 2): 3, (20, 20): 0, (20, 21): 2, (20, 22): 2, (20, 23): 2, (20, 24): 2, (20, 25): 1, (20, 26): 1, (20, 3): 3, (20, 4): 3, (20, 5): 2, (20, 6): 2, (20, 7): 1, (20, 8): 2, (20, 9): 3, (20, None): None, (21, 1): 3, (21, 10): 3, (21, 11): 3, (21, 12): 2, (21, 13): 4, (21, 14): 2, (21, 15): 3, (21, 16): 2, (21, 17): 3, (21, 18): 1, (21, 19): 2, (21, 2): 3, (21, 20): 2, (21, 21): 0, (21, 22): 2, (21, 23): 1, (21, 24): 2, (21, 25): 1, (21, 26): 2, (21, 3): 2, (21, 4): 2, (21, 5): 1, (21, 6): 2, (21, 7): 2, (21, 8): 3, (21, 9): 3, (21, None): None, (22, 1): 1, (22, 10): 3, (22, 11): 3, (22, 12): 4, (22, 13): 2, (22, 14): 3, (22, 15): 2, (22, 16): 3, (22, 17): 2, (22, 18): 1, (22, 19): 2, (22, 2): 2, (22, 20): 2, (22, 21): 2, (22, 22): 0, (22, 23): 2, (22, 24): 1, (22, 25): 2, (22, 26): 1, (22, 3): 2, (22, 4): 3, (22, 5): 3, (22, 6): 3, (22, 7): 2, (22, 8): 2, (22, 9): 3, (22, None): None, (23, 1): 3, (23, 10): 2, (23, 11): 3, (23, 12): 2, (23, 13): 3, (23, 14): 2, (23, 15): 2, (23, 16): 2, (23, 17): 4, (23, 18): 1, (23, 19): 1, (23, 2): 2, (23, 20): 2, (23, 21): 1, (23, 22): 2, (23, 23): 0, (23, 24): 2, (23, 25): 2, (23, 26): 2, (23, 3): 1, (23, 4): 1, (23, 5): 1, (23, 6): 2, (23, 7): 3, (23, 8): 3, (23, 9): 3, (23, None): None, (24, 1): 1, (24, 10): 2, (24, 11): 3, (24, 12): 3, (24, 13): 2, (24, 14): 2, (24, 15): 2, (24, 16): 4, (24, 17): 2, (24, 18): 1, (24, 19): 1, (24, 2): 1, (24, 20): 2, (24, 21): 2, (24, 22): 1, (24, 23): 2, (24, 24): 0, (24, 25): 2, (24, 26): 2, (24, 3): 1, (24, 4): 2, (24, 5): 3, (24, 6): 3, (24, 7): 3, (24, 8): 2, (24, 9): 3, (24, None): None, (25, 1): 3, (25, 10): 3, (25, 11): 2, (25, 12): 2, (25, 13): 3, (25, 14): 2, (25, 15): 4, (25, 16): 2, (25, 17): 2, (25, 18): 1, (25, 19): 2, (25, 2): 3, (25, 20): 1, (25, 21): 1, (25, 22): 2, (25, 23): 2, (25, 24): 2, (25, 25): 0, (25, 26): 2, (25, 3): 3, (25, 4): 2, (25, 5): 1, (25, 6): 1, (25, 7): 1, (25, 8): 2, (25, 9): 3, (25, None): None, (26, 1): 1, (26, 10): 3, (26, 11): 2, (26, 12): 3, (26, 13): 2, (26, 14): 4, (26, 15): 2, (26, 16): 2, (26, 17): 2, (26, 18): 1, (26, 19): 2, (26, 2): 2, (26, 20): 1, (26, 21): 2, (26, 22): 1, (26, 23): 2, (26, 24): 2, (26, 25): 2, (26, 26): 0, (26, 3): 3, (26, 4): 3, (26, 5): 3, (26, 6): 2, (26, 7): 1, (26, 8): 1, (26, 9): 3, (26, None): None, (3, 1): 2, (3, 10): 1, (3, 11): 3, (3, 12): 2, (3, 13): 2, (3, 14): 1, (3, 15): 1, (3, 16): 3, (3, 17): 3, (3, 18): 2, (3, 19): 1, (3, 2): 1, (3, 20): 3, (3, 21): 2, (3, 22): 2, (3, 23): 1, (3, 24): 1, (3, 25): 3, (3, 26): 3, (3, 3): 0, (3, 4): 1, (3, 5): 2, (3, 6): 3, (3, 7): 4, (3, 8): 3, (3, 9): 2, (3, None): None, (4, 1): 3, (4, 10): 2, (4, 11): 3, (4, 12): 2, (4, 13): 3, (4, 14): 1, (4, 15): 2, (4, 16): 2, (4, 17): 3, (4, 18): 2, (4, 19): 2, (4, 2): 2, (4, 20): 3, (4, 21): 2, (4, 22): 3, (4, 23): 1, (4, 24): 2, (4, 25): 2, (4, 26): 3, (4, 3): 1, (4, 4): 0, (4, 5): 1, (4, 6): 2, (4, 7): 3, (4, 8): 4, (4, 9): 2, (4, None): None, (5, 1): 4, (5, 10): 2, (5, 11): 2, (5, 12): 1, (5, 13): 3, (5, 14): 1, (5, 15): 3, (5, 16): 1, (5, 17): 3, (5, 18): 2, (5, 19): 2, (5, 2): 3, (5, 20): 2, (5, 21): 1, (5, 22): 3, (5, 23): 1, (5, 24): 3, (5, 25): 1, (5, 26): 3, (5, 3): 2, (5, 4): 1, (5, 5): 0, (5, 6): 1, (5, 7): 2, (5, 8): 3, (5, 9): 2, (5, None): None, (6, 1): 3, (6, 10): 3, (6, 11): 2, (6, 12): 2, (6, 13): 3, (6, 14): 2, (6, 15): 3, (6, 16): 1, (6, 17): 2, (6, 18): 2, (6, 19): 3, (6, 2): 4, (6, 20): 2, (6, 21): 2, (6, 22): 3, (6, 23): 2, (6, 24): 3, (6, 25): 1, (6, 26): 2, (6, 3): 3, (6, 4): 2, (6, 5): 1, (6, 6): 0, (6, 7): 1, (6, 8): 2, (6, 9): 2, (6, None): None, (7, 1): 2, (7, 10): 3, (7, 11): 1, (7, 12): 2, (7, 13): 2, (7, 14): 3, (7, 15): 3, (7, 16): 1, (7, 17): 1, (7, 18): 2, (7, 19): 3, (7, 2): 3, (7, 20): 1, (7, 21): 2, (7, 22): 2, (7, 23): 3, (7, 24): 3, (7, 25): 1, (7, 26): 1, (7, 3): 4, (7, 4): 3, (7, 5): 2, (7, 6): 1, (7, 7): 0, (7, 8): 1, (7, 9): 2, (7, None): None, (8, 1): 1, (8, 10): 3, (8, 11): 2, (8, 12): 3, (8, 13): 2, (8, 14): 3, (8, 15): 2, (8, 16): 2, (8, 17): 1, (8, 18): 2, (8, 19): 3, (8, 2): 2, (8, 20): 2, (8, 21): 3, (8, 22): 2, (8, 23): 3, (8, 24): 2, (8, 25): 2, (8, 26): 1, (8, 3): 3, (8, 4): 4, (8, 5): 3, (8, 6): 2, (8, 7): 1, (8, 8): 0, (8, 9): 2, (8, None): None, (9, 1): 2, (9, 10): 1, (9, 11): 1, (9, 12): 1, (9, 13): 1, (9, 14): 1, (9, 15): 1, (9, 16): 1, (9, 17): 1, (9, 18): 4, (9, 19): 3, (9, 2): 2, (9, 20): 3, (9, 21): 3, (9, 22): 3, (9, 23): 3, (9, 24): 3, (9, 25): 3, (9, 26): 3, (9, 3): 2, (9, 4): 2, (9, 5): 2, (9, 6): 2, (9, 7): 2, (9, 8): 2, (9, 9): 0, (9, None): None, (None, 1): None, (None, 10): None, (None, 11): None, (None, 12): None, (None, 13): None, (None, 14): None, (None, 15): None, (None, 16): None, (None, 17): None, (None, 18): None, (None, 19): None, (None, 2): None, (None, 20): None, (None, 21): None, (None, 22): None, (None, 23): None, (None, 24): None, (None, 25): None, (None, 26): None, (None, 3): None, (None, 4): None, (None, 5): None, (None, 6): None, (None, 7): None, (None, 8): None, (None, 9): None, (None, None): None}
DL = 21
DR = 22
F = 3
FL = 4
FR = 2
INT_TO_CODE = {1: 'right', 10: 'upfront', 11: 'upback', 12: 'upleft', 13: 'upright', 14: 'upfrontleft', 15: 'upfrontright', 16: 'upbackleft', 17: 'upbackright', 18: 'down', 19: 'downfront', 2: 'frontright', 20: 'downback', 21: 'downleft', 22: 'downright', 23: 'downfrontleft', 24: 'downfrontright', 25: 'downbackleft', 26: 'downbackright', 3: 'front', 4: 'frontleft', 5: 'left', 6: 'backleft', 7: 'back', 8: 'backright', 9: 'up', None: 'unknown'}
INT_TO_NICE = {1: 'Right', 10: 'Up-Front', 11: 'Up-Back', 12: 'Up-Left', 13: 'Up-Right', 14: 'Up-Front-Left', 15: 'Up-Front-Right', 16: 'Up-Back-Left', 17: 'Up-Back-Right', 18: 'Down', 19: 'Down-Front', 2: 'Front-Right', 20: 'Down-Back', 21: 'Down-Left', 22: 'Down-Right', 23: 'Down-Front-Left', 24: 'Down-Front-Right', 25: 'Down-Back-Left', 26: 'Down-Back-Right', 3: 'Front', 4: 'Front-Left', 5: 'Left', 6: 'Back-Left', 7: 'Back', 8: 'Back-Right', 9: 'Up', None: 'Unknown'}
L = 5
class NICE

Bases: object

B = 'Back'
BL = 'Back-Left'
BR = 'Back-Right'
D = 'Down'
DB = 'Down-Back'
DBL = 'Down-Back-Left'
DBR = 'Down-Back-Right'
DF = 'Down-Front'
DFL = 'Down-Front-Left'
DFR = 'Down-Front-Right'
DL = 'Down-Left'
DR = 'Down-Right'
F = 'Front'
FL = 'Front-Left'
FR = 'Front-Right'
L = 'Left'
R = 'Right'
U = 'Up'
UB = 'Up-Back'
UBL = 'Up-Back-Left'
UBR = 'Up-Back-Right'
UF = 'Up-Front'
UFL = 'Up-Front-Left'
UFR = 'Up-Front-Right'
UL = 'Up-Left'
UNKNOWN = 'Unknown'
UR = 'Up-Right'
NICE_TO_CODE = {'Back': 'back', 'Back-Left': 'backleft', 'Back-Right': 'backright', 'Down': 'down', 'Down-Back': 'downback', 'Down-Back-Left': 'downbackleft', 'Down-Back-Right': 'downbackright', 'Down-Front': 'downfront', 'Down-Front-Left': 'downfrontleft', 'Down-Front-Right': 'downfrontright', 'Down-Left': 'downleft', 'Down-Right': 'downright', 'Front': 'front', 'Front-Left': 'frontleft', 'Front-Right': 'frontright', 'Left': 'left', 'Right': 'right', 'Unknown': 'unknown', 'Up': 'up', 'Up-Back': 'upback', 'Up-Back-Left': 'upbackleft', 'Up-Back-Right': 'upbackright', 'Up-Front': 'upfront', 'Up-Front-Left': 'upfrontleft', 'Up-Front-Right': 'upfrontright', 'Up-Left': 'upleft', 'Up-Right': 'upright'}
NICE_TO_INT = {'Back': 7, 'Back-Left': 6, 'Back-Right': 8, 'Down': 18, 'Down-Back': 20, 'Down-Back-Left': 25, 'Down-Back-Right': 26, 'Down-Front': 19, 'Down-Front-Left': 23, 'Down-Front-Right': 24, 'Down-Left': 21, 'Down-Right': 22, 'Front': 3, 'Front-Left': 4, 'Front-Right': 2, 'Left': 5, 'Right': 1, 'Unknown': None, 'Up': 9, 'Up-Back': 11, 'Up-Back-Left': 16, 'Up-Back-Right': 17, 'Up-Front': 10, 'Up-Front-Left': 14, 'Up-Front-Right': 15, 'Up-Left': 12, 'Up-Right': 13}
R = 1
U = 9
UB = 11
UBL = 16
UBR = 17
UF = 10
UFL = 14
UFR = 15
UL = 12
UNKNOWN = None
UR = 13
d = None
f1 = None
f2 = None
graphid.core.color_nodes(graph, labelattr='label', brightness=0.878, outof=None, sat_adjust=None)[source]

Colors edges and nodes by nid

graphid.core.demo_refresh()[source]

CommandLine

python -m graphid.core.refresh demo_refresh \
        --num_pccs=40 --size=2 --show

Example

>>> # ENABLE_DOCTEST
>>> from graphid.core.refresh import *  # NOQA
>>> demo_refresh()
>>> util.show_if_requested()
graphid.core.nx_ensure_agraph_color(graph)[source]

changes colors to hex strings on graph attrs

graphid.core.on_pick(event, infr=None)[source]