diff --git a/src/aind_behavior_dynamic_foraging/task_logic/interventions/bias_intervention.py b/src/aind_behavior_dynamic_foraging/task_logic/interventions/bias_intervention.py index 2b570bf2..ddb5e9b4 100644 --- a/src/aind_behavior_dynamic_foraging/task_logic/interventions/bias_intervention.py +++ b/src/aind_behavior_dynamic_foraging/task_logic/interventions/bias_intervention.py @@ -83,12 +83,13 @@ def are_antibias_conditions_met(self, bias: float) -> bool: return False if self.trials_in_bias_intervention > self.parameters.intervention_interval: - if bias <= self.parameters.threshold.lower: - logger.debug("Bias calculated below threshold: %s." % bias) + if abs(bias) >= self.parameters.threshold.upper: + logger.debug("Bias calculated above threshold: %s." % bias) return True - if bias >= self.parameters.threshold.upper: - logger.debug("Bias calculated above threshold: %s." % bias) + # bias intervention only when the spout is currently off-center. + if abs(bias) <= self.parameters.threshold.lower and self.total_lickspout_offset != 0: + logger.debug("Bias calculated below threshold: %s." % bias) return True self.trials_in_bias_intervention += 1 return False diff --git a/src/aind_behavior_dynamic_foraging/task_logic/trial_generators/block_based_trial_generator.py b/src/aind_behavior_dynamic_foraging/task_logic/trial_generators/block_based_trial_generator.py index e00e993f..417a9442 100644 --- a/src/aind_behavior_dynamic_foraging/task_logic/trial_generators/block_based_trial_generator.py +++ b/src/aind_behavior_dynamic_foraging/task_logic/trial_generators/block_based_trial_generator.py @@ -219,6 +219,7 @@ def next(self) -> Trial | None: is_auto_reward_right, lickspout_offset_delta = self.bias_intervention.determine_antibias_intervention( self.bias ) + reward_fraction = ( 1 if is_auto_reward_right is None else self.spec.bias_intervention_parameters.reward_fraction ) @@ -269,13 +270,21 @@ def _are_autowater_conditions_met(self) -> bool: min_ignore = self.spec.autowater_parameters.min_ignored_trials min_unreward = self.spec.autowater_parameters.min_unrewarded_trials + if min_ignore == 0 or min_unreward == 0: + logger.debug( + "Autowater enabled every trial (min_ignored_trials=%s, min_unrewarded_trials=%s).", + min_ignore, + min_unreward, + ) + return True + is_ignored = [choice is None for choice in self.is_right_choice_history] - if len(is_ignored) > min_ignore and all(is_ignored[-min_ignore:]): + if len(is_ignored) >= min_ignore and all(is_ignored[-min_ignore:]): logger.debug("Past %s trials ignored." % min_ignore) return True is_unrewarded = [not reward for reward in self.reward_history] - if len(is_unrewarded) > min_unreward and all(is_unrewarded[-min_unreward:]): + if len(is_unrewarded) >= min_unreward and all(is_unrewarded[-min_unreward:]): logger.debug("Past %s trials unrewarded." % min_unreward) return True diff --git a/src/aind_behavior_dynamic_foraging/task_logic/trial_generators/coupled_trial_generators/coupled_warmup_trial_generator.py b/src/aind_behavior_dynamic_foraging/task_logic/trial_generators/coupled_trial_generators/coupled_warmup_trial_generator.py index f041c432..1ccf7da5 100644 --- a/src/aind_behavior_dynamic_foraging/task_logic/trial_generators/coupled_trial_generators/coupled_warmup_trial_generator.py +++ b/src/aind_behavior_dynamic_foraging/task_logic/trial_generators/coupled_trial_generators/coupled_warmup_trial_generator.py @@ -100,5 +100,6 @@ def _is_block_switch_allowed(self) -> bool: bool indicating whether block can switch """ - reward_count = sum([outcome.is_rewarded for outcome in self.outcome_history]) + block_outcomes = self.outcome_history[-self.trials_in_block :] if self.trials_in_block > 0 else [] + reward_count = sum(outcome.is_rewarded for outcome in block_outcomes) return reward_count >= self.spec.min_block_reward diff --git a/tests/test_interventions/test_bias_intervention.py b/tests/test_interventions/test_bias_intervention.py index 5dc9ba92..8665b642 100644 --- a/tests/test_interventions/test_bias_intervention.py +++ b/tests/test_interventions/test_bias_intervention.py @@ -39,12 +39,21 @@ def test_returns_true_when_bias_above_upper_threshold(self): self.assertTrue(result) - def test_returns_true_when_bias_below_lower_threshold(self): - """Intervention when bias is below threshold""" + def test_returns_false_when_bias_below_lower_threshold_and_centered(self): + """No intervention is needed at low bias when the lickspout is already centered.""" bias_intervention = BiasIntervention(BiasInterventionParameters(bias_window_length=5)) bias_intervention.trials_in_bias_intervention = 15 result = bias_intervention.are_antibias_conditions_met(0.2) + self.assertFalse(result) + + def test_returns_true_when_bias_below_lower_threshold_and_offset_exists(self): + """Low bias should trigger recentering when lickspout has drifted from center.""" + bias_intervention = BiasIntervention(BiasInterventionParameters(bias_window_length=5)) + bias_intervention.trials_in_bias_intervention = 15 + bias_intervention.total_lickspout_offset = 0.2 + result = bias_intervention.are_antibias_conditions_met(0.2) + self.assertTrue(result) def test_gives_right_water_on_left_bias(self):