Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions docs/trials_table_mapping.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,13 @@ Columns are grouped by the raw source they map from.

### From `TrialOutcome.json` (`SoftwareEvents` stream)

> **Note:** For `is_auto_response_right`, `True` means right and `False`
> **Note:** For `is_auto_reward_right`, `True` means right and `False`
> means left.

| Trials column | Mapping |
| --- | --- |
| `auto_waterL` / `auto_waterR` | From `is_auto_response_right`. `1` on the auto-responded side; `0` on the other side, when there was no auto-response (`None`), or when the trial is missing. |
| `bait_left` / `bait_right` | Boolean. `bait_right` is `True` if `p_reward_right == 1` and `is_auto_response_right` is `None` or `False`. `bait_left` is `True` if `p_reward_left == 1` and `is_auto_response_right` is `None` or `True`. |
| `auto_waterL` / `auto_waterR` | From `is_auto_reward_right`. `1` on the auto-responded side; `0` on the other side, when there was no auto-response (`None`), or when the trial is missing. |
| `bait_left` / `bait_right` | Boolean. `bait_right` is `True` if `p_reward_right == 1` and `is_auto_reward_right` is `None` or `False`. `bait_left` is `True` if `p_reward_left == 1` and `is_auto_reward_right` is `None` or `True`. |
| `response_duration` | `response_deadline_duration`. |
| `reward_consumption_duration` | `Trial -> reward_consumption_duration`. |
| `reward_probabilityL` / `reward_probabilityR` | The **block** probability from `Trial -> metadata -> p_reward_left` / `p_reward_right`. The top-level `trial.p_reward_left` / `p_reward_right` is the per-trial probability, not the block probability, so it is not used here. `None` when the trial or its metadata is missing. |
Expand Down Expand Up @@ -152,6 +152,6 @@ These were mapped during exploration but are no longer in scope:
| Date | Change |
| --- | --- |
| 2026-06-17 | `animal_response` now decodes the `Response` event's `{ "Item1": <time>, "Item2": <choice> }` payload via `Item2` (`True` → right `1`, `False` → left `0`, missing/`None` → no response `2`), rather than treating the whole payload as the choice. |
| 2026-06-17 | `auto_waterL` / `auto_waterR` now encode no auto-response (`is_auto_response_right` is `None`) and missing trials as `0` instead of `NULL`. The columns are non-nullable (`int`, default `0`). |
| 2026-06-17 | `auto_waterL` / `auto_waterR` now encode no auto-response (`is_auto_reward_right` is `None`) and missing trials as `0` instead of `NULL`. The columns are non-nullable (`int`, default `0`). |
| 2026-06-20 | Added `reward_size_left` / `reward_size_right` (reward volume in uL) from `task_parameters.reward_size`, and `side_bias` from the per-trial `TrialMetrics` event (`bias` field). |
| 2026-06-20 | `reward_probabilityL` / `reward_probabilityR` now read the block probability from `trial.metadata.p_reward_left` / `p_reward_right` instead of the top-level per-trial `trial.p_reward_left` / `p_reward_right`. |
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ readme = "README.md"
version = "0.0.0"

dependencies = [
"aind-behavior-dynamic-foraging[data] @ git+https://github.com/AllenNeuralDynamics/Aind.Behavior.DynamicForaging.git@f517d14ff965763a8f713b8a1be5dfb26b9312e1",
"aind-behavior-dynamic-foraging[data] @ git+https://github.com/AllenNeuralDynamics/Aind.Behavior.DynamicForaging.git@baab12133b22f599c1ba0583260eca9eca216cc0",
"ipykernel",
]

Expand Down
22 changes: 11 additions & 11 deletions src/dynamic_foraging_processing/processing/_trial_table.py
Original file line number Diff line number Diff line change
Expand Up @@ -327,18 +327,18 @@ def _is_baited(trial: t.Optional[Trial], *, is_right: bool) -> bool:
probability is ``1``) *and* the trial was not auto-responded to *that
same* port.

``is_auto_response_right`` encodes the auto-response: ``True`` means the
``is_auto_reward_right`` encodes the auto-response: ``True`` means the
trial was auto-responded to the right, ``False`` to the left, and
``None`` means there was no auto-response.

In plain English, for the right port the conditions are:

* ``p_reward_right == 1`` — reward is certain on the right, and
* the trial was *not* auto-responded to the right
(``is_auto_response_right`` is ``None`` or ``False``).
(``is_auto_reward_right`` is ``None`` or ``False``).

The left port is the mirror image (``p_reward_left == 1`` and not
auto-responded to the left, i.e. ``is_auto_response_right`` is ``None``
auto-responded to the left, i.e. ``is_auto_reward_right`` is ``None``
or ``True``).

Parameters
Expand All @@ -358,25 +358,25 @@ def _is_baited(trial: t.Optional[Trial], *, is_right: bool) -> bool:
--------
Right port guaranteed reward, no auto-response → baited:

>>> trial = Trial(p_reward_right=1, p_reward_left=0, is_auto_response_right=None)
>>> trial = Trial(p_reward_right=1, p_reward_left=0, is_auto_reward_right=None)
>>> TrialTableBuilder._is_baited(trial, is_right=True)
True

Same trial, but auto-responded to the right collects (forfeits) the bait:

>>> trial = Trial(p_reward_right=1, p_reward_left=0, is_auto_response_right=True)
>>> trial = Trial(p_reward_right=1, p_reward_left=0, is_auto_reward_right=True)
>>> TrialTableBuilder._is_baited(trial, is_right=True)
False

Left port without guaranteed reward → not baited:

>>> trial = Trial(p_reward_right=1, p_reward_left=0, is_auto_response_right=None)
>>> trial = Trial(p_reward_right=1, p_reward_left=0, is_auto_reward_right=None)
>>> TrialTableBuilder._is_baited(trial, is_right=False)
False
"""
if trial is None:
return False
auto = trial.is_auto_response_right
auto = trial.is_auto_reward_right
if is_right:
# Right stays baited unless the animal was auto-responded right.
return trial.p_reward_right == 1 and auto in (None, False)
Expand All @@ -385,16 +385,16 @@ def _is_baited(trial: t.Optional[Trial], *, is_right: bool) -> bool:

@staticmethod
def _auto_water(trial: t.Optional[Trial], *, is_right: bool) -> int:
"""Encode autowater for a side from ``is_auto_response_right``.
"""Encode autowater for a side from ``is_auto_reward_right``.

Returns ``1`` if the auto response was to the requested side, else ``0``.
A missing trial or no auto-response (``is_auto_response_right`` is
A missing trial or no auto-response (``is_auto_reward_right`` is
``None``) counts as no autowater (``0``). ``is_right`` is ``True`` for
right.
"""
if trial is None or trial.is_auto_response_right is None:
if trial is None or trial.is_auto_reward_right is None:
return 0
return int(trial.is_auto_response_right is is_right)
return int(trial.is_auto_reward_right is is_right)

@staticmethod
def _block_reward_probability(trial: t.Optional[Trial], *, is_right: bool) -> t.Optional[float]:
Expand Down
4 changes: 2 additions & 2 deletions src/dynamic_foraging_processing/utils/rewards.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def get_annotated_rewards(
timestamps are correlated to the reward-delivery timestamps with
:func:`find_closest_timestamps`.
- ``automatic`` -- otherwise, when the matching trial auto-responded
(``is_auto_response_right is not None``).
(``is_auto_reward_right is not None``).
- ``earned`` -- otherwise (no matching trial, or no auto-response).

Parameters
Expand Down Expand Up @@ -80,7 +80,7 @@ def get_annotated_rewards(
for trial_index in trial_indices_in_reward_times:
outcome = _parse_outcome(trial_outcome_df.iloc[trial_index]["data"])
trial = outcome.trial if outcome is not None else None
if trial is None or trial.is_auto_response_right is None:
if trial is None or trial.is_auto_reward_right is None:
annotated_rewards.append("earned")
else:
annotated_rewards.append("automatic")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def _outcome_payload(auto) -> dict:
"reward_consumption_duration": 1.0,
"quiescence_period_duration": 0.5,
"inter_trial_interval_duration": 4.0,
"is_auto_response_right": auto,
"is_auto_reward_right": auto,
},
"is_right_choice": True,
"is_rewarded": True,
Expand Down
2 changes: 1 addition & 1 deletion tests/test_processing/test_trial_table.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ def _outcome(
"reward_consumption_duration": 1.0,
"quiescence_period_duration": 0.5,
"inter_trial_interval_duration": 4.0,
"is_auto_response_right": auto,
"is_auto_reward_right": auto,
}
if block_p_left is not None or block_p_right is not None:
trial["metadata"] = {"p_reward_left": block_p_left, "p_reward_right": block_p_right}
Expand Down
4 changes: 2 additions & 2 deletions tests/test_utils/test_rewards.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def _outcome_payload(auto=None) -> dict:
"reward_consumption_duration": 1.0,
"quiescence_period_duration": 0.5,
"inter_trial_interval_duration": 4.0,
"is_auto_response_right": auto,
"is_auto_reward_right": auto,
},
"is_right_choice": True,
"is_rewarded": True,
Expand All @@ -46,7 +46,7 @@ def test_get_annotated_rewards_marks_default_trials_as_earned():


def test_get_annotated_rewards_marks_auto_response_trials_as_automatic():
"""Trials with ``is_auto_response_right`` set (either side) are ``automatic``."""
"""Trials with ``is_auto_reward_right`` set (either side) are ``automatic``."""
reward_times = np.array([0.15, 0.42])
trial_outcome_df = _trial_outcome_df(np.array([0.1, 0.4]), autos=[True, False])

Expand Down
4 changes: 2 additions & 2 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading