Skip to content

Commit

Permalink
Merge pull request #15 from broadinstitute/fail-count-assertion-message
Browse files Browse the repository at this point in the history
Add error message explaining why probe count constraint cannot be satisfied
  • Loading branch information
haydenm authored Jul 6, 2018
2 parents 544bce7 + 307d25e commit af7929e
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 1 deletion.
48 changes: 47 additions & 1 deletion catch/pool/param_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,12 @@ def _total_probe_count_without_interp(params, probe_counts):
return s


class CannotSatisfyProbeCountConstraintError(Exception):
"""The search yielded a number of probes that exceeds the given constraint
"""
pass


def _round_params(params, probe_counts, max_total_count, loss_coeffs, weights,
mismatches_eps=0.01, cover_extension_eps=0.1,
mismatches_round=1, cover_extension_round=1):
Expand Down Expand Up @@ -434,12 +440,33 @@ def _round_params(params, probe_counts, max_total_count, loss_coeffs, weights,

total_probe_count = ic._make_total_probe_count_across_datasets_fn(
probe_counts, interp_fn_type='standard')

# Verify that the probe count satisfies the constraint
# Note that this assertion may fail if we are dealing with datasets
# for which few actual probe counts have been computed; in these
# cases, the interpolation may severely underestimate the number
# of probes at a particular parameter choice
assert total_probe_count(params_rounded) < max_total_count
# It may also fail if it is just not possible to satisfy the desired
# constraint given the precomputed probe counts (e.g., the total probe
# count for the most tolerant precomputed parameter values still exceeds
# the constraint)
tpc = total_probe_count(params_rounded)
if tpc > max_total_count:
msg = ("The total probe count based on parameter values found "
"in the search (%d) exceeds the given limit (%d). This "
"is likely to happen if the range of the precomputed "
"parameter values is not as large as it needs to be to "
"satisfy the constraint. That is, one or more parameter "
"values may need to be more loose to obtain %d probes. To "
"fix this, try inputting probe counts for a larger range "
"(in particular, less stringent choices) of parameter "
"values. Also, note that the search interpolates probe "
"counts between precomputed parameter values (%d may be an "
"interpolated count) and, if the precomputed parameter values "
"are too sparse (i.e., too few actual probe counts were "
"input), it may be underestimating the true number of probes "
"required." % (tpc, max_total_count, max_total_count, tpc))
raise CannotSatisfyProbeCountConstraintError(msg)

# Keep decreasing parameters while satisfying the constraint.
# In particular, choose to decrease the parameter whose reduction
Expand Down Expand Up @@ -685,4 +712,23 @@ def higher_dimensional_search(param_names, probe_counts, max_total_count,
probe_counts, interp_fn_type='nd')(x_sol)
x_sol_loss = loss_fn(x_sol, 0)

# Verify that the probe count satisfies the constraint
if x_sol_count > max_total_count:
msg = ("The total probe count based on parameter values found "
"in the search (%d) exceeds the given limit (%d). This "
"is likely to happen if the range of the precomputed "
"parameter values is not as large as it needs to be to "
"satisfy the constraint. That is, one or more parameter "
"values may need to be more loose to obtain %d probes. To "
"fix this, try inputting probe counts for a larger range "
"(in particular, less stringent choices) of parameter "
"values. Also, note that the search interpolates probe "
"counts between precomputed parameter values (%d may be an "
"interpolated count) and, if the precomputed parameter values "
"are too sparse (i.e., too few actual probe counts were "
"input), it may be underestimating the true number of probes "
"required." % (x_sol_count, max_total_count, max_total_count,
x_sol_count))
raise CannotSatisfyProbeCountConstraintError(msg)

return (x_sol_dict, x_sol_count, x_sol_loss)
21 changes: 21 additions & 0 deletions catch/pool/tests/test_param_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,12 @@ def _search_vwafr_typical_counts(self, search_fn):
hiv1_mismatches, hiv1_cover_extension = opt_params['hiv1_without_ltr']
self.assertTrue(hiv1_mismatches > 3 or hiv1_cover_extension > 20)

def _search_vwafr_too_small_counts(self, search_fn):
self.assertEqual(self.param_names_vwafr, ('mismatches', 'cover_extension'))
for max_total_count in [1, 1000, 10000]:
with self.assertRaises(param_search.CannotSatisfyProbeCountConstraintError):
search_fn(max_total_count)

def test_standard_search_vwafr_typical_counts(self):
"""Integration test with the V-WAfr probe set data."""
def search_fn(max_total_count):
Expand All @@ -146,6 +152,13 @@ def test_standard_search_vwafr_high_count(self):
self.assertEqual(mismatches, 0)
self.assertEqual(cover_extension, 0)

def test_standard_search_vwafr_too_small_counts(self):
"""Integration test with the V-WAfr probe set data."""
def search_fn(max_total_count):
return param_search.standard_search(self.probe_counts_vwafr,
max_total_count)
self._search_vwafr_too_small_counts(search_fn)

def test_higher_dimensional_search_vwafr_typical_counts(self):
"""Integration test with the V-WAfr probe set data."""
def search_fn(max_total_count):
Expand All @@ -154,6 +167,14 @@ def search_fn(max_total_count):
max_total_count, loss_coeffs=(1.0, 1.0/100.0))
self._search_vwafr_typical_counts(search_fn)

def test_higher_dimensional_search_vwafr_too_small_counts(self):
"""Integration test with the V-WAfr probe set data."""
def search_fn(max_total_count):
return param_search.higher_dimensional_search(
self.param_names_vwafr, self.probe_counts_vwafr,
max_total_count, loss_coeffs=(1.0, 1.0/100.0))
self._search_vwafr_too_small_counts(search_fn)

def test_higher_dimensional_search_vwafr_with_third_param(self):
"""Integration test with the V-WAfr probe set data.
Expand Down

0 comments on commit af7929e

Please sign in to comment.