Skip to content

Commit

Permalink
API/TST: Add 'hybrid' link_strategy and separate tests
Browse files Browse the repository at this point in the history
This makes 'hybrid' (numba + recursive for trivial subnets) the new default link_strategy.
'hybrid' and 'numba' are tested separately, so that the numba linker is still being tested fully
on small subnets.
  • Loading branch information
nkeim committed Apr 8, 2018
1 parent 2e809a7 commit 5f58b5e
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 22 deletions.
25 changes: 11 additions & 14 deletions trackpy/linking/linking.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@ def link_iter(coords_iter, search_range, **kwargs):
Reduce search_range by multiplying it by this factor.
neighbor_strategy : {'KDTree', 'BTree'}
algorithm used to identify nearby features. Default 'KDTree'.
link_strategy : {'recursive', 'nonrecursive', 'numba', 'drop', 'auto'}
link_strategy : {'recursive', 'nonrecursive', 'hybrid', 'numba', 'drop', 'auto'}
algorithm used to resolve subnetworks of nearby particles
'auto' uses numba if available
'auto' uses hybrid (numba+recursive) if available
'drop' causes particles in subnetworks to go unlinked
dist_func : function, optional
a custom distance function that takes two 1D arrays of coordinates and
Expand Down Expand Up @@ -135,9 +135,9 @@ def link(f, search_range, pos_columns=None, t_column='frame', **kwargs):
Reduce search_range by multiplying it by this factor.
neighbor_strategy : {'KDTree', 'BTree'}
algorithm used to identify nearby features. Default 'KDTree'.
link_strategy : {'recursive', 'nonrecursive', 'numba', 'drop', 'auto'}
link_strategy : {'recursive', 'nonrecursive', 'numba', 'hybrid', 'drop', 'auto'}
algorithm used to resolve subnetworks of nearby particles
'auto' uses numba if available
'auto' uses hybrid (numba+recursive) if available
'drop' causes particles in subnetworks to go unlinked
dist_func : function, optional
a custom distance function that takes two 1D arrays of coordinates and
Expand Down Expand Up @@ -221,9 +221,9 @@ def link_df_iter(f_iter, search_range, pos_columns=None,
Reduce search_range by multiplying it by this factor.
neighbor_strategy : {'KDTree', 'BTree'}
algorithm used to identify nearby features. Default 'KDTree'.
link_strategy : {'recursive', 'nonrecursive', 'numba', 'drop', 'auto'}
link_strategy : {'recursive', 'nonrecursive', 'numba', 'hybrid', 'drop', 'auto'}
algorithm used to resolve subnetworks of nearby particles
'auto' uses numba if available
'auto' uses hybrid (numba+recursive) if available
'drop' causes particles in subnetworks to go unlinked
dist_func : function, optional
a custom distance function that takes two 1D arrays of coordinates and
Expand Down Expand Up @@ -374,14 +374,17 @@ def __init__(self, search_range, memory=0, predictor=None,

if link_strategy is None or link_strategy == 'auto':
if NUMBA_AVAILABLE:
link_strategy = 'numba'
link_strategy = 'hybrid'
else:
link_strategy = 'recursive'

if link_strategy == 'recursive':
subnet_linker = subnet_linker_recursive
elif link_strategy == 'numba':
elif link_strategy == 'hybrid':
subnet_linker = subnet_linker_numba
elif link_strategy == 'numba':
subnet_linker = functools.partial(subnet_linker_numba,
hybrid=False)
elif link_strategy == 'nonrecursive':
subnet_linker = subnet_linker_nonrecursive
elif link_strategy == 'drop':
Expand Down Expand Up @@ -433,12 +436,6 @@ def update_hash(self, coords, t, extra_data=None):
for m in self.mem_set:
# add points to the hash
prev_hash.add_point(m)
# Record how many times this particle got "held back".
# Since this particle has already been yielded in a previous
# level, we can't store it there. We'll have to put it in the
# track object, then copy this info to the point in cur_hash
# if/when we make a link.
m.track.incr_memory()
# re-create the forward_cands list
m.forward_cands = []

Expand Down
16 changes: 8 additions & 8 deletions trackpy/linking/subnetlinker.py
Original file line number Diff line number Diff line change
Expand Up @@ -427,19 +427,19 @@ def subnet_linker_nonrecursive(source_set, dest_set, search_range, **kwargs):

return sn_spl, sn_dpl


def subnet_linker_numba(source_set, dest_set, search_range, **kwargs):
def subnet_linker_numba(source_set, dest_set, search_range,
hybrid=True, **kwargs):
"""Link a subnet using a numba-accelerated algorithm.
Since this is meant to be the highest-performance option, it
has some special behaviors:
- Each source particle's forward_cands must be sorted by distance.
- Subnets with only 1 source or destination particle, or with at
most 4 source particles and 4 destination particles, are
solved using the recursive pure-Python algorithm, which has
much less overhead since it does not convert to a numpy
representation.
- If the 'hybrid' option is true, subnets with only 1 source or
destination particle, or with at most 4 source particles and
4 destination particles, are solved using the recursive
pure-Python algorithm, which has much less overhead since
it does not convert to a numpy representation.
"""
lss = len(source_set)
lds = len(dest_set)
Expand All @@ -458,7 +458,7 @@ def subnet_linker_numba(source_set, dest_set, search_range, **kwargs):
_s.forward_cands.append((None, search_range))

# Shortcut for small subnets, because the numba linker has significant overhead
if lds == 1 or lss == 1 or (lds <= 4 and lss <= 4):
if (lds == 1 or lss == 1 or (lds <= 4 and lss <= 4)) and hybrid:
sn_spl, sn_dpl = recursive_linker_obj(source_set, lds, search_range, **kwargs)
else:
sn_spl, sn_dpl = numba_link(source_set, lds, search_range, **kwargs)
Expand Down
6 changes: 6 additions & 0 deletions trackpy/tests/test_linking.py
Original file line number Diff line number Diff line change
Expand Up @@ -701,6 +701,12 @@ def setUp(self):
self.linker_opts = dict(link_strategy='numba')


class TestHybridLink(SubnetNeededTests):
def setUp(self):
_skip_if_no_numba()
self.linker_opts = dict(link_strategy='hybrid')


class TestNonrecursiveLink(SubnetNeededTests):
def setUp(self):
self.linker_opts = dict(link_strategy='nonrecursive')
Expand Down

0 comments on commit 5f58b5e

Please sign in to comment.