graphid.core.annot_inference module

graphid.core.annot_inference._rectify_decision(evidence_decision, meta_decision)[source]

If evidence decision is not explicitly set, then meta decision is used to make a guess. Raises a ValueError if decisions are in incompatible states.

class graphid.core.annot_inference.Consistency[source]

Bases: object

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

inconsistent_components(graph=None)[source]

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

consistent_components(graph=None)[source]

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

Yields:

cc – set: nodes within the PCC

class graphid.core.annot_inference.Feedback[source]

Bases: object

_check_edge(edge)[source]
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..

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

add_node_feedback(aid, **attrs)[source]
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
_print_debug_ccs()[source]
feedback_keys = ['evidence_decision', 'tags', 'user_id', 'meta_decision', 'timestamp_c1', 'timestamp_c2', 'timestamp_s1', 'timestamp', 'confidence', 'num_reviews', 'review_id']
feedback_data_keys = ['evidence_decision', 'tags', 'user_id', 'meta_decision', 'timestamp_c1', 'timestamp_c2', 'timestamp_s1', 'timestamp', 'confidence']
apply_feedback_edges()[source]

Transforms the feedback dictionaries into nx graph edge attributes. This

_rectify_feedback(feedback)[source]
_rectify_feedback_item(vals)[source]

uses most recently use strategy

all_feedback_items()[source]
all_feedback()[source]
clear_feedback(edges=None)[source]

Delete all edges properties related to feedback

clear_edges()[source]

Removes all edges from the graph

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

clear_name_labels()[source]

Sets all annotation node name labels to be unknown

class graphid.core.annot_inference.NameRelabel[source]

Bases: object

node_label(aid)[source]
node_labels(*aids)[source]
_next_nid()[source]
_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]

_rectified_relabel(cc_subgraphs)[source]

Reuses as many names as possible

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.annot_inference.MiscHelpers[source]

Bases: object

_rectify_nids(aids, nids)[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()
>>> # xdoc: +REQUIRES(--show)
>>> util.qtensure()
>>> after = infr
>>> before.show(fnum=1, pnum=(1, 2, 1), pickable=True)
>>> after.show(fnum=1, pnum=(1, 2, 2), pickable=True)
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]
update_node_attributes(aids=None, nids=None)[source]
initialize_graph(graph=None)[source]

Constructs the internal networkx Graph objects

print(msg, level=1, color=None)[source]
latest_logs(colored=False)[source]
dump_logs()[source]
class graphid.core.annot_inference.AltConstructors[source]

Bases: object

_graph_cls

alias of NiceGraph

classmethod from_pairs(aid_pairs, attrs=None, verbose=False)[source]
classmethod from_netx(G, verbose=False, infer=True)[source]

Creates an AnnotInference object from a networkx graph

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.annot_inference.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)>
>>> # xdoc: +REQUIRES(--show)
>>> infr.show_graph()
>>> util.show_if_requested()
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}
copy()[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.

set_config(config, **kw)[source]