From 2ff3cfd7b50f6490cb81c6e12194a67d1f5e41b1 Mon Sep 17 00:00:00 2001 From: han-lab Date: Tue, 18 Jun 2024 16:29:16 -0700 Subject: [PATCH 1/5] feat: duplicate for 2.2 --- .../curriculums/coupled_baiting_2p2.py | 418 ++++++++++++++++ .../curriculums/uncoupled_baiting_2p2.py | 402 +++++++++++++++ .../curriculums/uncoupled_no_baiting_2p2.py | 400 +++++++++++++++ .../uncoupled_no_baiting_2p2rwdDelay159.py | 470 ++++++++++++++++++ 4 files changed, 1690 insertions(+) create mode 100644 code/aind_auto_train/curriculums/coupled_baiting_2p2.py create mode 100644 code/aind_auto_train/curriculums/uncoupled_baiting_2p2.py create mode 100644 code/aind_auto_train/curriculums/uncoupled_no_baiting_2p2.py create mode 100644 code/aind_auto_train/curriculums/uncoupled_no_baiting_2p2rwdDelay159.py diff --git a/code/aind_auto_train/curriculums/coupled_baiting_2p2.py b/code/aind_auto_train/curriculums/coupled_baiting_2p2.py new file mode 100644 index 0000000..338fb3f --- /dev/null +++ b/code/aind_auto_train/curriculums/coupled_baiting_2p2.py @@ -0,0 +1,418 @@ +''' +Curriculum for Dynamic Foraging - Coupled Baiting +https://alleninstitute.sharepoint.com/:p:/s/NeuralDynamics/EQwuU0I4PBtGsU2wilCHklEBDTXYGT3F-QtaN6iDGJLBmg?e=N10dya + +Run the code to generate the curriculum.json and graphs + +''' + +# %% +from aind_auto_train.curriculum_manager import LOCAL_SAVED_CURRICULUM_ROOT + +from aind_auto_train.schema.curriculum import ( + DynamicForagingCurriculum, StageTransitions, TransitionRule, + Decision +) +from aind_auto_train.schema.task import ( + Task, TrainingStage, DynamicForagingParas, + AutoWaterMode, AdvancedBlockMode +) +from aind_auto_train import setup_logging +setup_logging() + +curriculum_name = Task.C1B1 +curriculum_version = "2.1" +curriculum_description = '''2024-05-09 decrease delay period as we now use much longer early lick punishment''' + +task_url = "https://github.com/AllenNeuralDynamics/dynamic-foraging-task" +task_schema_version = "1.1.0" + +# --- Parameters --- +# Stage 1 with warmup (classical Stage 1.1 + 1.2) +paras_stage_1_warmup = DynamicForagingParas( + # Metainfo + task_url=task_url, + task_schema_version=task_schema_version, + task=Task.C1B1, + training_stage=TrainingStage.STAGE_1_WARMUP, # "Phase B" in Han's slides + description="Warmup, followed by Phase B in Han's slides (block = [10, 20, 5], p_sum = 0.8, p_ratio = [1:0])", + + # -- Essentials -- + # Warmup ON + warmup='on', + warm_min_trial=50, + warm_max_choice_ratio_bias=0.1, + warm_min_finish_ratio=0.8, + warm_windowsize=20, + + # p_sum = 0.8, p_ratio = [1:0] + BaseRewardSum=0.8, + RewardFamily=3, + RewardPairsN=1, + + # block = [10, 20, 5] + BlockMin=10, + BlockMax=20, + BlockBeta=5, + BlockMinReward=0, + + # Small ITI at the beginning to better engage the animal + ITIMin=1, + ITIMax=7, + ITIBeta=3, + + # Add a (fixed) small delay period at the beginning # TODO: automate delay period + DelayMin=0.1, + DelayMax=0.1, + DelayBeta=0, + + # Reward size and reward delay + RewardDelay=0.0, + RightValue_volume=4.0, + LeftValue_volume=4.0, + + # -- Within session automation -- + # Auto water + AutoReward=True, + AutoWaterType=AutoWaterMode.NATURAL, + Unrewarded=3, + Ignored=3, + Multiplier=0.5, + + # Auto block + AdvancedBlockAuto=AdvancedBlockMode.NOW, + SwitchThr=0.5, + PointsInARow=5, + + # Auto stop; set StopIgnores to a large number at the beginning + MaxTrial=1000, + MaxTime=90, + StopIgnores=20000, + + # -- Miscs -- + ResponseTime=5.0, # Very long response time at the beginning + RewardConsumeTime=1.0, # Shorter RewardConsumeTime to increase the number of trials + UncoupledReward="", # Only valid in uncoupled task +) + +transition_from_stage_1_warmup = StageTransitions( + from_stage=TrainingStage.STAGE_1_WARMUP, + transition_rules=[ + TransitionRule( + decision=Decision.PROGRESS, + to_stage=TrainingStage.STAGE_2, + condition_description="Finished trials >= 200 and efficiency >= 0.6", + condition="""lambda metrics: + metrics.finished_trials[-1] >= 200 + and + metrics.foraging_efficiency[-1] >= 0.6 + """, + ), + TransitionRule( + decision=Decision.PROGRESS, + to_stage=TrainingStage.STAGE_1, + condition_description="After the first session", + condition="""lambda metrics: + metrics.session_at_current_stage >= 1 + """, + ) + ] +) + +# Stage 1 without warmup (classical 1.2) +paras_stage_1 = DynamicForagingParas( + **{ + **paras_stage_1_warmup.model_dump(), + **dict( + training_stage=TrainingStage.STAGE_1, + description="Phase B in Han's slides (block = [10, 20, 5], p_sum = 0.8, p_ratio = [1:0])", + + # -- Essentials -- + # Turn off Warmup from now on + warmup='off', + + Unrewarded=5, + Ignored=5, + + # Decrease water size to 3.0 from now on + RightValue_volume=2.0, + LeftValue_volume=2.0, + ) + } +) + +transition_from_stage_1 = StageTransitions( + from_stage=TrainingStage.STAGE_1, + transition_rules=[ + TransitionRule( + decision=Decision.PROGRESS, + to_stage=TrainingStage.STAGE_2, + condition_description="Finished trials >= 200 and efficiency >= 0.6", + condition="""lambda metrics: + metrics.finished_trials[-1] >= 200 + and + metrics.foraging_efficiency[-1] >= 0.6 + """, + ) + ] +) + +# "Phase C" in Han's slides +paras_stage_2 = DynamicForagingParas( + **{ + **paras_stage_1.model_dump(), + **dict( + training_stage=TrainingStage.STAGE_2, + description="Phase C in Han's slides (block = [10, 40, 10], p_sum = 0.6, p_ratio = [8:1])", + + # --- Only include changes compared to stage_1 --- + # -- Essentials -- + # p_sum = 0.8 --> 0.6, p_ratio = [1:0] -> [8:1] + BaseRewardSum=0.6, + RewardFamily=1, + RewardPairsN=1, + + # block length [10, 20, 5] --> [10, 40, 10] + BlockMin=10, + BlockMax=40, + BlockBeta=10, + + # ITI [1, 7, 3] --> [1, 10, 5] + ITIMin=1, + ITIMax=10, + ITIBeta=3, + + DelayMin=0.3, + DelayMax=0.3, + + # -- Within session automation -- + # Decrease auto water: unrewarded 5 --> 10, ignored 5 --> 10 + Unrewarded=10, + Ignored=10, + + # Increase auto block switch threshold: 0.5 --> 0.6 + SwitchThr=0.6, + StopIgnores=50, # Auto stop on ignores-in-a-row starts to take effect + + # Miscs + ResponseTime=3, # Decrease response time: 5 --> 3 + ) + } +) + +transition_from_stage_2 = StageTransitions( + from_stage=TrainingStage.STAGE_2, + transition_rules=[ + TransitionRule( + decision=Decision.PROGRESS, + to_stage=TrainingStage.STAGE_3, + condition_description="Finished trials >= 300 and efficiency >= 0.65", + condition="""lambda metrics: + metrics.finished_trials[-1] >= 300 + and + metrics.foraging_efficiency[-1] >= 0.65 + """, + ), + TransitionRule( + decision=Decision.ROLLBACK, + to_stage=TrainingStage.STAGE_1, + condition_description="Finished trials < 200 or efficiency < 0.55", + condition="""lambda metrics: + metrics.finished_trials[-1] < 200 + or + metrics.foraging_efficiency[-1] < 0.55 + """, + ), + ] +) + +# "Phase D" in Han's slides +paras_stage_3 = DynamicForagingParas( + **{ + **paras_stage_2.model_dump(), + **dict( + training_stage=TrainingStage.STAGE_3, + description="Phase D in Han's slides (block = [10, 40, 10], p_sum = 0.45, p_ratio = [8:1])", + + # -- Essentials -- + # p_sum = 0.6 --> 0.45, p_ratio still [8:1] + BaseRewardSum=0.45, + + # block length [10, 40, 10] --> [20, 60, 20] + BlockMin=20, + BlockMax=60, + BlockBeta=20, + + # ITI [2, 10, 5] --> [3, 15, 5] + ITIMin=1, + ITIMax=15, + ITIBeta=3, + + DelayMin=0.5, + DelayMax=0.5, + DelayBeta=0.0, + + # Decrease autowater number (almost turned off) + Unrewarded=15, + Ignored=15, + + # Miscs + ResponseTime=2, # Decrease response time: 3 --> 2 + ) + } +) + +transition_from_stage_3 = StageTransitions( + from_stage=TrainingStage.STAGE_3, + transition_rules=[ + TransitionRule( + decision=Decision.PROGRESS, + to_stage=TrainingStage.STAGE_FINAL, + condition_description="Finished trials >= 400 and efficiency >= 0.7", + condition="""lambda metrics: + metrics.finished_trials[-1] >= 400 + and + metrics.foraging_efficiency[-1] >= 0.7 + """, + ), + TransitionRule( + decision=Decision.ROLLBACK, + to_stage=TrainingStage.STAGE_2, + condition_description="Finished trials < 300 or efficiency < 0.65", + condition="""lambda metrics: + metrics.finished_trials[-1] < 300 + or + metrics.foraging_efficiency[-1] < 0.65 + """, + ), + ] +) + +# "Phase E" in Han's slides +paras_stage_final = DynamicForagingParas( + **{ + **paras_stage_3.model_dump(), + **dict( + training_stage=TrainingStage.STAGE_FINAL, + description="Phase E in Han's slides (full task: block = [20, 60, 20], p_sum = 0.45, p_ratio = [8:1], [6:1], [3:1], [1:1])", + + # --- Here I explicitly list all parameters again just for clarity --- + # Essentials + + # Warmup OFF + warmup='off', + + # p_sum = 0.45, p_ratio = [8:1] --> [8:1], [6:1], [3:1], [1:1] + BaseRewardSum=0.45, + RewardFamily=1, + RewardPairsN=4, + + # block = [10, 20, 5] (mean ~ 33 trials) + BlockMin=20, + BlockMax=60, + BlockBeta=20, + BlockMinReward=0, + + # ITI [1, 15, 5] --> [1, 30, 5] (mean ~ 6.0 s, not included 1-s no lick window before ITI start) + ITIMin=1, + ITIMax=30, + ITIBeta=3, + + DelayMin=1.0, + DelayMax=1.0, + DelayBeta=0.0, + + # Reward size and reward delay + RewardDelay=0.0, + RightValue_volume=2.0, + LeftValue_volume=2.0, + + # Within session automation + AutoReward=False, # Turn off auto water + AdvancedBlockAuto=AdvancedBlockMode.OFF, # Turn off auto block + + MaxTrial=1000, + MaxTime=90, + StopIgnores=50, + + # Miscs + ResponseTime=1.0, + RewardConsumeTime=3.0, + UncoupledReward="", # Only valid in uncoupled task + ) + } +) + +transition_from_stage_final = StageTransitions( + from_stage=TrainingStage.STAGE_FINAL, + transition_rules=[ + TransitionRule( + # For graduation, obviously we need more requirements. + decision=Decision.PROGRESS, + to_stage=TrainingStage.GRADUATED, + condition_description=("For recent 5 sessions," + "mean finished trials >= 500 and mean efficiency >= 0.70 " + "and total sessions >= 10 and sessions at final >= 5"), + condition="""lambda metrics: + metrics.session_total >= 10 + and + metrics.session_at_current_stage >= 5 + and + np.mean(metrics.finished_trials[-5:]) >= 500 + and + np.mean(metrics.foraging_efficiency[-5:]) >= 0.70 + """, + ), + TransitionRule( + decision=Decision.ROLLBACK, + to_stage=TrainingStage.STAGE_3, + condition_description="For recent 2 sessions, mean finished trials < 350 or efficiency < 0.65", + condition="""lambda metrics: + np.mean(metrics.finished_trials[-2:]) < 350 + or + np.mean(metrics.foraging_efficiency[-2:]) < 0.65 + """, + ), + ] +) + +# --- Curriculum --- +# %% +curriculum = DynamicForagingCurriculum( + curriculum_name=curriculum_name, + curriculum_version=curriculum_version, + curriculum_description=curriculum_description, + + parameters={ + TrainingStage.STAGE_1_WARMUP: paras_stage_1_warmup, + TrainingStage.STAGE_1: paras_stage_1, + TrainingStage.STAGE_2: paras_stage_2, + TrainingStage.STAGE_3: paras_stage_3, + TrainingStage.STAGE_FINAL: paras_stage_final, + TrainingStage.GRADUATED: paras_stage_final, + }, + + curriculum={ + TrainingStage.STAGE_1_WARMUP: transition_from_stage_1_warmup, + TrainingStage.STAGE_1: transition_from_stage_1, + TrainingStage.STAGE_2: transition_from_stage_2, + TrainingStage.STAGE_3: transition_from_stage_3, + TrainingStage.STAGE_FINAL: transition_from_stage_final, + }, + +) + +# %% +if __name__ == '__main__': + import os + + curriculum_path = LOCAL_SAVED_CURRICULUM_ROOT + os.makedirs(curriculum_path, exist_ok=True) + + # Save curriculum json and diagrams + curriculum.save_to_json(path=curriculum_path) + curriculum.diagram_rules(path=curriculum_path, + render_file_format='svg') + curriculum.diagram_paras(path=curriculum_path, + render_file_format='svg', + fontsize=12) diff --git a/code/aind_auto_train/curriculums/uncoupled_baiting_2p2.py b/code/aind_auto_train/curriculums/uncoupled_baiting_2p2.py new file mode 100644 index 0000000..c8a7b00 --- /dev/null +++ b/code/aind_auto_train/curriculums/uncoupled_baiting_2p2.py @@ -0,0 +1,402 @@ +''' +Curriculum for Dynamic Foraging - Uncoupled Baiting +Adapted from the Uncoupled Without Baiting curriculum +"draft 2, began using 10/17/23" +https://alleninstitute-my.sharepoint.com/:w:/g/personal/katrina_nguyen_alleninstitute_org/EUGu5FS565pLuHhqFYT9yfEBjF7tIDGVEmbwnmcCYJBoWw?e=wD8fX9 + +Run the code to generate the curriculum.json and graphs + +''' + +# %% +from aind_auto_train.curriculum_manager import LOCAL_SAVED_CURRICULUM_ROOT + +from aind_auto_train.schema.curriculum import ( + DynamicForagingCurriculum, StageTransitions, TransitionRule, + Decision +) +from aind_auto_train.schema.task import ( + Task, TrainingStage, DynamicForagingParas, + AutoWaterMode, AdvancedBlockMode +) +from aind_auto_train import setup_logging +setup_logging() + +# Note this could be any string, not necessarily one of the Task enums +curriculum_name = "Uncoupled Baiting" +curriculum_version = "2.1" +curriculum_description = '''2024-05-09 decrease delay period as we now use much longer early lick punishment''' + +task_url = "https://github.com/AllenNeuralDynamics/dynamic-foraging-task" +task_schema_version = "1.1.0" + +# --- Parameters --- +# Stage 1 with warmup (classical Stage 1.1 + 1.2) + +paras_stage_1_warmup = DynamicForagingParas( + # Metainfo + training_stage=TrainingStage.STAGE_1_WARMUP, + description="Warmup, followed by legendary Coupled Baiting Stage 1.2 (block = [10, 30, 10], p_sum = 0.8, p_ratio = [1:0])", + + # -- Essentials -- + # Warmup ON + warmup='on', + warm_min_trial=50, + warm_max_choice_ratio_bias=0.1, + warm_min_finish_ratio=0.8, + warm_windowsize=20, + + # First session is ** coupled baiting ** + task_url=task_url, + task_schema_version=task_schema_version, + task=Task.C1B1, + + # p_sum = 0.8, p_ratio = [1:0] + BaseRewardSum=0.8, + RewardFamily=3, + RewardPairsN=1, + + # block = [10, 30, 10] + BlockMin=10, + BlockMax=30, + BlockBeta=10, + BlockMinReward=0, + + ITIMin=1, + ITIMax=7, + ITIBeta=3, + + # Add a (fixed) small delay period at the beginning # TODO: automate delay period + DelayMin=0.1, + DelayMax=0.1, + DelayBeta=0, + + # Reward size and reward delay + RewardDelay=0.0, + RightValue_volume=4.0, + LeftValue_volume=4.0, + + # -- Within session automation -- + # Auto water + AutoReward=True, + AutoWaterType=AutoWaterMode.NATURAL, + Unrewarded=3, + Ignored=3, + Multiplier=0.5, + + # Auto block + AdvancedBlockAuto=AdvancedBlockMode.NOW, + SwitchThr=0.5, + PointsInARow=5, + + # Auto stop; set StopIgnores to a large number at the beginning + MaxTrial=1000, + MaxTime=90, + StopIgnores=20000, + + # -- Miscs -- + ResponseTime=5.0, # Very long response time at the beginning + RewardConsumeTime=1.0, # Shorter RewardConsumeTime to increase the number of trials + UncoupledReward="", # Only valid in uncoupled task +) + +transition_from_stage_1_warmup = StageTransitions( + from_stage=TrainingStage.STAGE_1_WARMUP, + transition_rules=[ + TransitionRule( + decision=Decision.PROGRESS, + to_stage=TrainingStage.STAGE_2, + condition_description="Finished trials >= 200 and efficiency >= 0.6", + condition="""lambda metrics: + metrics.finished_trials[-1] >= 200 + and + metrics.foraging_efficiency[-1] >= 0.6 + """, + ), + TransitionRule( + decision=Decision.PROGRESS, + to_stage=TrainingStage.STAGE_1, + condition_description="After the first session", + condition="""lambda metrics: + metrics.session_at_current_stage >= 1 + """, + ) + + ] +) + +# Stage 1 without warmup (classical 1.2) +paras_stage_1 = DynamicForagingParas( + **{ + **paras_stage_1_warmup.model_dump(), + **dict( + training_stage=TrainingStage.STAGE_1, + description="Phase B in Han's slides (block = [10, 30, 10], p_sum = 0.8, p_ratio = [1:0])", + + # -- Essentials -- + # Turn off Warmup from now on + warmup='off', + + Unrewarded=5, + Ignored=5, + + # Decrease water size to 2.0 from now on + RightValue_volume=2.0, + LeftValue_volume=2.0, + ) + } +) + +transition_from_stage_1 = StageTransitions( + from_stage=TrainingStage.STAGE_1, + transition_rules=[ + TransitionRule( + decision=Decision.PROGRESS, + to_stage=TrainingStage.STAGE_2, + condition_description="Finished trials >= 200 and efficiency >= 0.6", + condition="""lambda metrics: + metrics.finished_trials[-1] >= 200 + and + metrics.foraging_efficiency[-1] >= 0.6 + """, + ) + ] +) + +paras_stage_2 = DynamicForagingParas( + **{ + **paras_stage_1.model_dump(), + **dict( + training_stage=TrainingStage.STAGE_2, + description="Coupled baiting (block = [20, 35, 10], p_sum = 0.8, p_ratio = [8:1])", + + # --- Only include changes compared to stage_1 --- + # -- Essentials -- + + # Coupled baiting + task=Task.C1B1, + + # p_ratio [1:0] -> [8:1] + RewardFamily=1, + RewardPairsN=1, + + # Decrease autowater + Unrewarded=10, + Ignored=10, + + # block length [10, 30, 10] --> [20, 35, 20] + BlockMin=20, + BlockMax=35, + BlockBeta=10, + + # ITI [1, 7, 3] --> [1, 10, 3] + ITIMax=10, + + DelayMin=0.3, + DelayMax=0.3, + + # -- Within session automation -- + # Miscs + ResponseTime=3.0, # Decrease response time: 5 --> 3 + ) + } +) + +transition_from_stage_2 = StageTransitions( + from_stage=TrainingStage.STAGE_2, + transition_rules=[ + TransitionRule( + decision=Decision.PROGRESS, + to_stage=TrainingStage.STAGE_3, + condition_description="Finished trials >= 300 and efficiency >= 0.65 and stay for >= 2 days", + condition="""lambda metrics: + metrics.finished_trials[-1] >= 300 + and + metrics.foraging_efficiency[-1] >= 0.65 + and + metrics.session_at_current_stage >= 2 + """, + ), + TransitionRule( + decision=Decision.ROLLBACK, + to_stage=TrainingStage.STAGE_1, + condition_description="Finished trials < 200 or efficiency < 0.55", + condition="""lambda metrics: + metrics.finished_trials[-1] < 200 + or + metrics.foraging_efficiency[-1] < 0.55 + """, + ), + ] +) + +paras_stage_3 = DynamicForagingParas( + **{ + **paras_stage_2.model_dump(), + **dict( + training_stage=TrainingStage.STAGE_3, + description="Switch to uncoupled but still baiting; p_rew = [0.1, 0.4, 0.7]; turn on auto water for 1 day", + + # -- Essentials -- + # Coupled baiting + task=Task.C0B1, + UncoupledReward="0.1, 0.4, 0.7", + + DelayMin=0.5, + DelayMax=0.5, + DelayBeta=0.0, + + # Final block length for uncoupled task + BlockMin=20, + BlockMax=35, + BlockBeta=10, + + # ITI [1, 10, 3] --> [1, 15, 3] + ITIMax=15, + + # Turn on auto water for the first day after switching to uncoupled task + AutoReward=True, + Unrewarded=15, # almost turned off + Ignored=15, # almost turned off + + # Turn off auto block + AdvancedBlockAuto=AdvancedBlockMode.OFF, # Turn off auto block + + # -- Within session automation -- + # Miscs + ResponseTime=2.0, # Decrease response time: 3 --> 2 + + ) + } +) + +transition_from_stage_3 = StageTransitions( + from_stage=TrainingStage.STAGE_3, + transition_rules=[ + TransitionRule( + decision=Decision.PROGRESS, + to_stage=TrainingStage.STAGE_FINAL, + condition_description="Just stay for 1 day", + condition="""lambda metrics: + metrics.session_at_current_stage >= 1 + """, + ), + # Once we reach here (C0B0), maybe we should not roll back to C1B0 or C1B1 anymore? + ] +) + +paras_stage_final = DynamicForagingParas( + **{ + **paras_stage_3.model_dump(), + **dict( + training_stage=TrainingStage.STAGE_FINAL, + description="Uncoupled baiting; p_rew = [0.1, 0.4, 0.7]; turn off auto water", + + # Essentials + # Coupled baiting + task=Task.C0B1, + UncoupledReward="0.1, 0.4, 0.7", + + BlockMin=20, + BlockMax=35, + BlockBeta=10, + BlockMinReward=0, + + ITIMin=1.0, + ITIMax=30.0, + ITIBeta=3.0, + + DelayMin=1.0, + DelayMax=1.0, + DelayBeta=0.0, + + RewardDelay=0, + + # Within session automation + AutoReward=False, # Turn off auto water + AdvancedBlockAuto=AdvancedBlockMode.OFF, # Turn off auto block + + MaxTrial=1000, + MaxTime=90, + StopIgnores=20000, + + # Miscs + ResponseTime=1.0, + RewardConsumeTime=3.0, + ) + } +) + +transition_from_stage_final = StageTransitions( + from_stage=TrainingStage.STAGE_FINAL, + transition_rules=[ + TransitionRule( + # For graduation, obviously we need more requirements. + decision=Decision.PROGRESS, + to_stage=TrainingStage.GRADUATED, + condition_description=("For recent 5 sessions," + "mean finished trials >= 500 and mean efficiency >= 0.70 " + "and total sessions >= 10 and sessions at final >= 5"), + condition="""lambda metrics: + metrics.session_total >= 10 + and + metrics.session_at_current_stage >= 5 + and + np.mean(metrics.finished_trials[-5:]) >= 500 + and + np.mean(metrics.foraging_efficiency[-5:]) >= 0.70 + """, + ), + TransitionRule( + decision=Decision.ROLLBACK, + to_stage=TrainingStage.STAGE_3, # Back to C0B0 with auto water + condition_description="For recent 2 sessions, mean finished trials < 300 or efficiency < 0.6", + condition="""lambda metrics: + np.mean(metrics.finished_trials[-2:]) < 300 + or + np.mean(metrics.foraging_efficiency[-2:]) < 0.6 + """, + ), + ] +) + +# --- Curriculum --- +# %% +curriculum = DynamicForagingCurriculum( + curriculum_name=curriculum_name, + curriculum_version=curriculum_version, + curriculum_description=curriculum_description, + + parameters={ + TrainingStage.STAGE_1_WARMUP: paras_stage_1_warmup, + TrainingStage.STAGE_1: paras_stage_1, + TrainingStage.STAGE_2: paras_stage_2, + TrainingStage.STAGE_3: paras_stage_3, + TrainingStage.STAGE_FINAL: paras_stage_final, + TrainingStage.GRADUATED: paras_stage_final, + }, + + curriculum={ + TrainingStage.STAGE_1_WARMUP: transition_from_stage_1_warmup, + TrainingStage.STAGE_1: transition_from_stage_1, + TrainingStage.STAGE_2: transition_from_stage_2, + TrainingStage.STAGE_3: transition_from_stage_3, + TrainingStage.STAGE_FINAL: transition_from_stage_final, + }, + +) + +# %% +if __name__ == '__main__': + import os + + curriculum_path = LOCAL_SAVED_CURRICULUM_ROOT + os.makedirs(curriculum_path, exist_ok=True) + + # Save curriculum json and diagrams + curriculum.save_to_json(path=curriculum_path) + curriculum.diagram_rules(path=curriculum_path, + render_file_format='svg') + curriculum.diagram_paras(path=curriculum_path, + render_file_format='svg', + fontsize=12) diff --git a/code/aind_auto_train/curriculums/uncoupled_no_baiting_2p2.py b/code/aind_auto_train/curriculums/uncoupled_no_baiting_2p2.py new file mode 100644 index 0000000..815c127 --- /dev/null +++ b/code/aind_auto_train/curriculums/uncoupled_no_baiting_2p2.py @@ -0,0 +1,400 @@ +''' +Curriculum for Dynamic Foraging - Uncoupled without Baiting +Adopted from "draft 2, began using 10/17/23" +https://alleninstitute-my.sharepoint.com/:w:/g/personal/katrina_nguyen_alleninstitute_org/EUGu5FS565pLuHhqFYT9yfEBjF7tIDGVEmbwnmcCYJBoWw?e=wD8fX9 + +Run the code to generate the curriculum.json and graphs + +''' + +# %% +from aind_auto_train.curriculum_manager import LOCAL_SAVED_CURRICULUM_ROOT + +from aind_auto_train.schema.curriculum import ( + DynamicForagingCurriculum, StageTransitions, TransitionRule, + Decision +) +from aind_auto_train.schema.task import ( + Task, TrainingStage, DynamicForagingParas, + AutoWaterMode, AdvancedBlockMode +) +from aind_auto_train import setup_logging +setup_logging() + +# Note this could be any string, not necessarily one of the Task enums +curriculum_name = Task.C0B0 +curriculum_version = "2.1" +curriculum_description = '''2024-05-09 decrease delay period as we now use much longer early lick punishment''' + +task_url = "https://github.com/AllenNeuralDynamics/dynamic-foraging-task" +task_schema_version = "1.1.0" + +# --- Parameters --- +# Stage 1 with warmup (classical Stage 1.1 + 1.2) + +paras_stage_1_warmup = DynamicForagingParas( + # Metainfo + training_stage=TrainingStage.STAGE_1_WARMUP, + description="Warmup, followed by legendary Coupled Baiting Stage 1.2 (block = [10, 30, 10], p_sum = 0.8, p_ratio = [1:0])", + + # -- Essentials -- + # Warmup ON + warmup='on', + warm_min_trial=50, + warm_max_choice_ratio_bias=0.1, + warm_min_finish_ratio=0.8, + warm_windowsize=20, + + # First session is ** coupled baiting ** + task_url=task_url, + task_schema_version=task_schema_version, + task=Task.C1B1, + + # p_sum = 0.8, p_ratio = [1:0] + BaseRewardSum=0.8, + RewardFamily=3, + RewardPairsN=1, + + # block = [10, 30, 10] + BlockMin=10, + BlockMax=30, + BlockBeta=10, + BlockMinReward=0, + + ITIMin=1, + ITIMax=7, + ITIBeta=3, + + # Add a (fixed) small delay period at the beginning # TODO: automate delay period + DelayMin=0.1, + DelayMax=0.1, + DelayBeta=0, + + # Reward size and reward delay + RewardDelay=0.0, + RightValue_volume=4.0, + LeftValue_volume=4.0, + + # -- Within session automation -- + # Auto water + AutoReward=True, + AutoWaterType=AutoWaterMode.NATURAL, + Unrewarded=3, + Ignored=3, + Multiplier=0.5, + + # Auto block + AdvancedBlockAuto=AdvancedBlockMode.NOW, + SwitchThr=0.5, + PointsInARow=5, + + # Auto stop; set StopIgnores to a large number at the beginning + MaxTrial=1000, + MaxTime=90, + StopIgnores=20000, + + # -- Miscs -- + ResponseTime=5.0, # Very long response time at the beginning + RewardConsumeTime=1.0, # Shorter RewardConsumeTime to increase the number of trials + UncoupledReward="", # Only valid in uncoupled task +) + +transition_from_stage_1_warmup = StageTransitions( + from_stage=TrainingStage.STAGE_1_WARMUP, + transition_rules=[ + TransitionRule( + decision=Decision.PROGRESS, + to_stage=TrainingStage.STAGE_2, + condition_description="Finished trials >= 200 and efficiency >= 0.6", + condition="""lambda metrics: + metrics.finished_trials[-1] >= 200 + and + metrics.foraging_efficiency[-1] >= 0.6 + """, + ), + TransitionRule( + decision=Decision.PROGRESS, + to_stage=TrainingStage.STAGE_1, + condition_description="After the first session", + condition="""lambda metrics: + metrics.session_at_current_stage >= 1 + """, + ) + + ] +) + +# Stage 1 without warmup (classical 1.2) +paras_stage_1 = DynamicForagingParas( + **{ + **paras_stage_1_warmup.model_dump(), + **dict( + training_stage=TrainingStage.STAGE_1, + description="Phase B in Han's slides (block = [10, 30, 10], p_sum = 0.8, p_ratio = [1:0])", + + # -- Essentials -- + # Turn off Warmup from now on + warmup='off', + + Unrewarded=5, + Ignored=5, + + # Decrease water size to 2.0 from now on + RightValue_volume=2.0, + LeftValue_volume=2.0, + ) + } +) + +transition_from_stage_1 = StageTransitions( + from_stage=TrainingStage.STAGE_1, + transition_rules=[ + TransitionRule( + decision=Decision.PROGRESS, + to_stage=TrainingStage.STAGE_2, + condition_description="Finished trials >= 200 and efficiency >= 0.6", + condition="""lambda metrics: + metrics.finished_trials[-1] >= 200 + and + metrics.foraging_efficiency[-1] >= 0.6 + """, + ) + ] +) + +paras_stage_2 = DynamicForagingParas( + **{ + **paras_stage_1.model_dump(), + **dict( + training_stage=TrainingStage.STAGE_2, + description="Coupled without baiting (block = [20, 35, 10], p_sum = 0.8, p_ratio = [8:1])", + + # --- Only include changes compared to stage_1 --- + # -- Essentials -- + + # Coupled no baiting + task=Task.C1B0, + + # p_ratio [1:0] -> [8:1] + RewardFamily=1, + RewardPairsN=1, + + # Decrease autowater + Unrewarded=10, + Ignored=10, + + # block length [10, 30, 10] --> [20, 35, 20] + BlockMin=20, + BlockMax=35, + BlockBeta=10, + + # ITI [1, 7, 3] --> [1, 10, 3] + ITIMax=10, + + # Delay still 0.1 + DelayMin=0.3, + DelayMax=0.3, + + # -- Within session automation -- + # Miscs + ResponseTime=3.0, # Decrease response time: 5 --> 3 + ) + } +) + +transition_from_stage_2 = StageTransitions( + from_stage=TrainingStage.STAGE_2, + transition_rules=[ + TransitionRule( + decision=Decision.PROGRESS, + to_stage=TrainingStage.STAGE_3, + condition_description="Finished trials >= 300 and efficiency >= 0.65 and stay for >= 2 days", + condition="""lambda metrics: + metrics.finished_trials[-1] >= 300 + and + metrics.foraging_efficiency[-1] >= 0.65 + and + metrics.session_at_current_stage >= 2 + """, + ), + TransitionRule( + decision=Decision.ROLLBACK, + to_stage=TrainingStage.STAGE_1, + condition_description="Finished trials < 200 or efficiency < 0.55", + condition="""lambda metrics: + metrics.finished_trials[-1] < 200 + or + metrics.foraging_efficiency[-1] < 0.55 + """, + ), + ] +) + +paras_stage_3 = DynamicForagingParas( + **{ + **paras_stage_2.model_dump(), + **dict( + training_stage=TrainingStage.STAGE_3, + description="Switch to uncoupled; p_rew = [0.1, 0.4, 0.7]; turn on auto water for 1 day", + + # -- Essentials -- + # Coupled no baiting + task=Task.C0B0, + UncoupledReward="0.1, 0.4, 0.7", + + DelayMin=0.5, + DelayMax=0.5, + DelayBeta=0.0, + + # Final block length for uncoupled task + BlockMin=20, + BlockMax=35, + BlockBeta=10, + + # ITI [1, 10, 3] --> [1, 15, 3] + ITIMax=15, + + # Turn on auto water for the first day after switching to uncoupled task + AutoReward=True, + Unrewarded=15, # almost turned off + Ignored=15, # almost turned off + + # Turn off auto block + AdvancedBlockAuto=AdvancedBlockMode.OFF, # Turn off auto block + + # Miscs + ResponseTime=2.0, # Decrease response time: 3 --> 2 + ) + } +) + +transition_from_stage_3 = StageTransitions( + from_stage=TrainingStage.STAGE_3, + transition_rules=[ + TransitionRule( + decision=Decision.PROGRESS, + to_stage=TrainingStage.STAGE_FINAL, + condition_description="Just stay for 1 day", + condition="""lambda metrics: + metrics.session_at_current_stage >= 1 + """, + ), + # Once we reach here (C0B0), maybe we should not roll back to C1B0 or C1B1 anymore? + ] +) + +paras_stage_final = DynamicForagingParas( + **{ + **paras_stage_3.model_dump(), + **dict( + training_stage=TrainingStage.STAGE_FINAL, + description="Uncoupled without baiting; p_rew = [0.1, 0.4, 0.7]; turn off auto water", + + # Essentials + # Coupled no baiting + task=Task.C0B0, + UncoupledReward="0.1, 0.4, 0.7", + + BlockMin=20, + BlockMax=35, + BlockBeta=10, + BlockMinReward=0, + + ITIMin=1.0, + ITIMax=30.0, + ITIBeta=3.0, + + DelayMin=1.0, + DelayMax=1.0, + DelayBeta=0.0, + + RewardDelay=0, + + # Within session automation + AutoReward=False, # Turn off auto water + AdvancedBlockAuto=AdvancedBlockMode.OFF, # Turn off auto block + + MaxTrial=1000, + MaxTime=90, + StopIgnores=20000, + + # Miscs + ResponseTime=1.0, + RewardConsumeTime=3.0, + ) + } +) + +transition_from_stage_final = StageTransitions( + from_stage=TrainingStage.STAGE_FINAL, + transition_rules=[ + TransitionRule( + # For graduation, obviously we need more requirements. + decision=Decision.PROGRESS, + to_stage=TrainingStage.GRADUATED, + condition_description=("For recent 5 sessions," + "mean finished trials >= 500 and mean efficiency >= 0.70 " + "and total sessions >= 10 and sessions at final >= 5"), + condition="""lambda metrics: + metrics.session_total >= 10 + and + metrics.session_at_current_stage >= 5 + and + np.mean(metrics.finished_trials[-5:]) >= 500 + and + np.mean(metrics.foraging_efficiency[-5:]) >= 0.70 + """, + ), + TransitionRule( + decision=Decision.ROLLBACK, + to_stage=TrainingStage.STAGE_3, # Back to C0B0 with auto water + condition_description="For recent 2 sessions, mean finished trials < 300 or efficiency < 0.6", + condition="""lambda metrics: + np.mean(metrics.finished_trials[-2:]) < 300 + or + np.mean(metrics.foraging_efficiency[-2:]) < 0.6 + """, + ), + ] +) + +# --- Curriculum --- +# %% +curriculum = DynamicForagingCurriculum( + curriculum_name=curriculum_name, + curriculum_version=curriculum_version, + curriculum_description=curriculum_description, + + parameters={ + TrainingStage.STAGE_1_WARMUP: paras_stage_1_warmup, + TrainingStage.STAGE_1: paras_stage_1, + TrainingStage.STAGE_2: paras_stage_2, + TrainingStage.STAGE_3: paras_stage_3, + TrainingStage.STAGE_FINAL: paras_stage_final, + TrainingStage.GRADUATED: paras_stage_final, + }, + + curriculum={ + TrainingStage.STAGE_1_WARMUP: transition_from_stage_1_warmup, + TrainingStage.STAGE_1: transition_from_stage_1, + TrainingStage.STAGE_2: transition_from_stage_2, + TrainingStage.STAGE_3: transition_from_stage_3, + TrainingStage.STAGE_FINAL: transition_from_stage_final, + }, + +) + +# %% +if __name__ == '__main__': + import os + + curriculum_path = LOCAL_SAVED_CURRICULUM_ROOT + os.makedirs(curriculum_path, exist_ok=True) + + # Save curriculum json and diagrams + curriculum.save_to_json(path=curriculum_path) + curriculum.diagram_rules(path=curriculum_path, + render_file_format='svg') + curriculum.diagram_paras(path=curriculum_path, + render_file_format='svg', + fontsize=12) diff --git a/code/aind_auto_train/curriculums/uncoupled_no_baiting_2p2rwdDelay159.py b/code/aind_auto_train/curriculums/uncoupled_no_baiting_2p2rwdDelay159.py new file mode 100644 index 0000000..911d80a --- /dev/null +++ b/code/aind_auto_train/curriculums/uncoupled_no_baiting_2p2rwdDelay159.py @@ -0,0 +1,470 @@ +''' +Curriculum for Dynamic Foraging - Uncoupled without Baiting +Adopted from "draft 2, began using 10/17/23" +https://alleninstitute-my.sharepoint.com/:w:/g/personal/katrina_nguyen_alleninstitute_org/EUGu5FS565pLuHhqFYT9yfEBjF7tIDGVEmbwnmcCYJBoWw?e=wD8fX9 + +Run the code to generate the curriculum.json and graphs +Added reward delay and shortened no-lick delay to take care of introduction of extra ITI when early licking + +''' + +#%% +from aind_auto_train.curriculum_manager import LOCAL_SAVED_CURRICULUM_ROOT +from aind_auto_train.schema.curriculum import ( + DynamicForagingCurriculum, StageTransitions, TransitionRule, + Decision +) +from aind_auto_train.schema.task import ( + Task, TrainingStage, DynamicForagingParas, + AutoWaterMode, AdvancedBlockMode +) +from aind_auto_train import setup_logging +setup_logging() + +# Note this could be any string, not necessarily one of the Task enums +curriculum_name = Task.C0B0 +curriculum_version = "2.1rwdDelay159" +curriculum_description = '''2024-05-09 added reward delay; decrease no-lick delay in early training; use [0.1, 0.5, 0.9]''' + +task_url = "https://github.com/AllenNeuralDynamics/dynamic-foraging-task" +task_schema_version = "1.1.0" + +# --- Parameters --- +# Stage 1 with warmup (classical Stage 1.1 + 1.2) + +paras_stage_1_warmup = DynamicForagingParas( + # Metainfo + training_stage=TrainingStage.STAGE_1_WARMUP, + description="Warmup, followed by legendary Coupled Baiting Stage 1.2 (block = [10, 30, 10], p_sum = 0.8, p_ratio = [1:0])", + + # -- Essentials -- + # Warmup ON + warmup='on', + warm_min_trial=50, + warm_max_choice_ratio_bias=0.1, + warm_min_finish_ratio=0.8, + warm_windowsize=20, + + # First session is ** coupled baiting ** + task_url=task_url, + task_schema_version=task_schema_version, + task=Task.C1B1, + + # p_sum = 0.8, p_ratio = [1:0] + BaseRewardSum=0.8, + RewardFamily=3, + RewardPairsN=1, + + # block = [10, 30, 10] + BlockMin=10, + BlockMax=30, + BlockBeta=10, + BlockMinReward=0, + + ITIMin=1, + ITIMax=7, + ITIBeta=3, + + # Add a (fixed) small delay period at the beginning # TODO: automate delay period + DelayMin=0, # almost turned off no lick window + DelayMax=0, + DelayBeta=0, + + # Reward size and reward delay + RewardDelay=0.1, + RightValue_volume=4.0, + LeftValue_volume=4.0, + + # -- Within session automation -- + # Auto water + AutoReward=True, + AutoWaterType=AutoWaterMode.NATURAL, + Unrewarded=3, + Ignored=3, + Multiplier=0.5, + + # Auto block + AdvancedBlockAuto=AdvancedBlockMode.NOW, + SwitchThr=0.5, + PointsInARow=5, + + # Auto stop; set StopIgnores to a large number at the beginning + MaxTrial=1000, + MaxTime=90, + StopIgnores=20000, + + # -- Miscs -- + ResponseTime=5.0, # Very long response time at the beginning + RewardConsumeTime=1.0, # Shorter RewardConsumeTime to increase the number of trials + UncoupledReward="", # Only valid in uncoupled task +) + +transition_from_stage_1_warmup = StageTransitions( + from_stage=TrainingStage.STAGE_1_WARMUP, + transition_rules=[ + TransitionRule( + decision=Decision.PROGRESS, + to_stage=TrainingStage.STAGE_2, + condition_description="Finished trials >= 200 and efficiency >= 0.6", + condition="""lambda metrics: + metrics.finished_trials[-1] >= 200 + and + metrics.foraging_efficiency[-1] >= 0.6 + """, + ), + TransitionRule( + decision=Decision.PROGRESS, + to_stage=TrainingStage.STAGE_1, + condition_description="After the first session", + condition="""lambda metrics: + metrics.session_at_current_stage >= 1 + """, + ) + + ] +) + +# Stage 1 without warmup (classical 1.2) +paras_stage_1 = DynamicForagingParas( + **{ + **paras_stage_1_warmup.model_dump(), + **dict( + training_stage=TrainingStage.STAGE_1, + description="Phase B in Han's slides (block = [10, 30, 10], p_sum = 0.8, p_ratio = [1:0])", + + # -- Essentials -- + # Turn off Warmup from now on + warmup='off', + + Unrewarded=5, + Ignored=5, + + # Decrease water size to 2.0 from now on + RightValue_volume=2.0, + LeftValue_volume=2.0, + ) + } +) + +transition_from_stage_1 = StageTransitions( + from_stage=TrainingStage.STAGE_1, + transition_rules=[ + TransitionRule( + decision=Decision.PROGRESS, + to_stage=TrainingStage.STAGE_2, + condition_description="Finished trials >= 200 and efficiency >= 0.6", + condition="""lambda metrics: + metrics.finished_trials[-1] >= 200 + and + metrics.foraging_efficiency[-1] >= 0.6 + """, + ) + ] +) + +paras_stage_2 = DynamicForagingParas( + **{ + **paras_stage_1.model_dump(), + **dict( + training_stage=TrainingStage.STAGE_2, + description="Coupled without baiting (block = [20, 35, 10], p_sum = 0.8, p_ratio = [8:1])", + + # --- Only include changes compared to stage_1 --- + # -- Essentials -- + + # Coupled no baiting + task=Task.C1B0, + + # p_ratio [1:0] -> [8:1] + RewardFamily=1, + RewardPairsN=1, + + # Decrease autowater + Unrewarded=10, + Ignored=10, + + # block length [10, 30, 10] --> [20, 35, 20] + BlockMin=20, + BlockMax=35, + BlockBeta=10, + + # ITI [1, 7, 3] --> [1, 10, 3] + ITIMax=10, + + # Delay 0.5 --> 1.0 + # DelayMin=0.5, + # DelayMax=0.5, + + # -- Within session automation -- + # Miscs + ResponseTime=1.5, # Decrease response time: 5 --> 1.5 + ) + } +) + +transition_from_stage_2 = StageTransitions( + from_stage=TrainingStage.STAGE_2, + transition_rules=[ + TransitionRule( + decision=Decision.PROGRESS, + to_stage=TrainingStage.STAGE_3, + condition_description="Stay for >= 3 days", + condition="""lambda metrics: + metrics.session_at_current_stage >= 3 + """, + ), + TransitionRule( + decision=Decision.ROLLBACK, + to_stage=TrainingStage.STAGE_1, + condition_description="Finished trials < 200 or efficiency < 0.55", + condition="""lambda metrics: + metrics.finished_trials[-1] < 200 + or + metrics.foraging_efficiency[-1] < 0.55 + """, + ), + ] +) + + +paras_stage_3 = DynamicForagingParas( + **{ + **paras_stage_2.model_dump(), + **dict( + training_stage=TrainingStage.STAGE_3, + description="Coupled without baiting (block = [20, 35, 10], p_sum = 0.8, p_ratio = [8:1]), turn on no lick window", + + # --- Only include changes compared to stage_1 --- + # -- Essentials -- + + # Coupled no baiting + task=Task.C1B0, + + # p_ratio [1:0] -> [8:1] + RewardFamily=1, + RewardPairsN=1, + + # Decrease autowater + Unrewarded=10, + Ignored=10, + + # block length [10, 30, 10] --> [20, 35, 20] + BlockMin=20, + BlockMax=35, + BlockBeta=10, + + # ITI [1, 7, 3] --> [1, 10, 3] + ITIMax=10, + + # Delay 0.5 --> 1.0 + DelayMin=1.0, + DelayMax=1.0, + + # -- Within session automation -- + # Miscs + ResponseTime=1.5, # Decrease response time: 5 --> 1.5 + ) + } +) + +transition_from_stage_3 = StageTransitions( + from_stage=TrainingStage.STAGE_3, + transition_rules=[ + TransitionRule( + decision=Decision.PROGRESS, + to_stage=TrainingStage.STAGE_4, + condition_description="Finished trials >= 300 and efficiency >= 0.65 and stay for >= 3 days", + condition="""lambda metrics: + metrics.finished_trials[-1] >= 300 + and + metrics.foraging_efficiency[-1] >= 0.65 + and + metrics.session_at_current_stage >= 3 + """, + ), + TransitionRule( + decision=Decision.ROLLBACK, + to_stage=TrainingStage.STAGE_1, + condition_description="Finished trials < 200 or efficiency < 0.55", + condition="""lambda metrics: + metrics.finished_trials[-1] < 200 + or + metrics.foraging_efficiency[-1] < 0.55 + """, + ), + ] +) + +paras_stage_4 = DynamicForagingParas( + **{ + **paras_stage_3.model_dump(), + **dict( + training_stage=TrainingStage.STAGE_4, + description="Switch to uncoupled; p_rew = [0.1, 0.4, 0.7] or [0.1, 0.5, 0.9]; turn on auto water for 1 days", + + # -- Essentials -- + # Uncoupled no baiting + task=Task.C0B0, + UncoupledReward="0.1, 0.5, 0.9", + + # reward delay + RewardDelay=0.15, # increased from 100ms + + # Final block length for uncoupled task + BlockMin=20, + BlockMax=35, + BlockBeta=10, + + # ITI [1, 10, 3] --> [1, 15, 3] + ITIMax=15, + + # Turn on auto water for the first day after switching to uncoupled task + AutoReward=True, + Unrewarded=15, # almost turned off + Ignored=15, # almost turned off + + # Turn off auto block + AdvancedBlockAuto=AdvancedBlockMode.OFF, # Turn off auto block + + # Miscs + ResponseTime=1.5, + ) + } +) + +transition_from_stage_4 = StageTransitions( + from_stage=TrainingStage.STAGE_4, + transition_rules=[ + TransitionRule( + decision=Decision.PROGRESS, + to_stage=TrainingStage.STAGE_FINAL, + condition_description="Just stay for 1 days", + condition="""lambda metrics: + metrics.session_at_current_stage >= 1 + """, + ), + # Once we reach here (C0B0), maybe we should not roll back to C1B0 or C1B1 anymore? + ] +) + +paras_stage_final = DynamicForagingParas( + **{ + **paras_stage_4.model_dump(), + **dict( + training_stage=TrainingStage.STAGE_FINAL, + description="Uncoupled without baiting; p_rew = [0.1, 0.5, 0.9]; turn off auto water", + + # Essentials + # Uncoupled no baiting + task=Task.C0B0, + UncoupledReward="0.1, 0.5, 0.9", + + BlockMin=20, + BlockMax=35, + BlockBeta=10, + BlockMinReward=0, + + ITIMin=2.0, + ITIMax=15.0, + ITIBeta=3.0, + + DelayMin=1.0, + DelayMax=1.0, + DelayBeta=0.0, + + RewardDelay=0.2, + + # Within session automation + AutoReward=False, # Turn off auto water + AdvancedBlockAuto=AdvancedBlockMode.OFF, # Turn off auto block + + MaxTrial=1000, + MaxTime=90, + StopIgnores=20000, + + # Miscs + ResponseTime=1.5, + RewardConsumeTime=1.0, + ) + } +) + +transition_from_stage_final = StageTransitions( + from_stage=TrainingStage.STAGE_FINAL, + transition_rules=[ + TransitionRule( + # For graduation, obviously we need more requirements. + decision=Decision.PROGRESS, + to_stage=TrainingStage.GRADUATED, + condition_description=("For recent 5 sessions," + "mean finished trials >= 500 and mean efficiency >= 0.70 " + "and total sessions >= 10 and sessions at final >= 5"), + condition="""lambda metrics: + metrics.session_total >= 10 + and + metrics.session_at_current_stage >= 5 + and + np.mean(metrics.finished_trials[-5:]) >= 500 + and + np.mean(metrics.foraging_efficiency[-5:]) >= 0.70 + """, + ), + TransitionRule( + decision=Decision.ROLLBACK, + to_stage=TrainingStage.STAGE_4, # Back to C0B0 with auto water + condition_description="For recent 2 sessions, mean finished trials < 300 or efficiency < 0.6", + condition="""lambda metrics: + np.mean(metrics.finished_trials[-2:]) < 300 + or + np.mean(metrics.foraging_efficiency[-2:]) < 0.6 + """, + ), + ] +) + +# --- Curriculum --- +# %% +curriculum = DynamicForagingCurriculum( + curriculum_name=curriculum_name, + curriculum_version=curriculum_version, + curriculum_description=curriculum_description, + + parameters={ + TrainingStage.STAGE_1_WARMUP: paras_stage_1_warmup, + TrainingStage.STAGE_1: paras_stage_1, + TrainingStage.STAGE_2: paras_stage_2, + TrainingStage.STAGE_3: paras_stage_3, + TrainingStage.STAGE_4: paras_stage_4, + TrainingStage.STAGE_FINAL: paras_stage_final, + TrainingStage.GRADUATED: paras_stage_final, + }, + + curriculum={ + TrainingStage.STAGE_1_WARMUP: transition_from_stage_1_warmup, + TrainingStage.STAGE_1: transition_from_stage_1, + TrainingStage.STAGE_2: transition_from_stage_2, + TrainingStage.STAGE_3: transition_from_stage_3, + TrainingStage.STAGE_4: transition_from_stage_4, + TrainingStage.STAGE_FINAL: transition_from_stage_final, + }, + +) + +# %% +if __name__ == '__main__': + #%% + import os + + curriculum_path = LOCAL_SAVED_CURRICULUM_ROOT + os.makedirs(curriculum_path, exist_ok=True) + + # Save curriculum json and diagrams + curriculum.save_to_json(path=curriculum_path) + curriculum.diagram_rules(path=curriculum_path, + render_file_format='svg') + #%% + curriculum.diagram_paras(path=curriculum_path, + render_file_format='svg', + fontsize=12) + +# %% From e1273ad90faa820bb05420504c2cb096c887c842 Mon Sep 17 00:00:00 2001 From: han-lab Date: Tue, 18 Jun 2024 16:32:43 -0700 Subject: [PATCH 2/5] feat: decrease stopIgnore to 25 for stage >= 2 --- code/aind_auto_train/curriculums/coupled_baiting_2p2.py | 4 ++-- code/aind_auto_train/curriculums/uncoupled_baiting_2p2.py | 4 +++- code/aind_auto_train/curriculums/uncoupled_no_baiting_2p2.py | 4 +++- .../curriculums/uncoupled_no_baiting_2p2rwdDelay159.py | 4 +++- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/code/aind_auto_train/curriculums/coupled_baiting_2p2.py b/code/aind_auto_train/curriculums/coupled_baiting_2p2.py index 338fb3f..e3dfcf7 100644 --- a/code/aind_auto_train/curriculums/coupled_baiting_2p2.py +++ b/code/aind_auto_train/curriculums/coupled_baiting_2p2.py @@ -192,7 +192,7 @@ # Increase auto block switch threshold: 0.5 --> 0.6 SwitchThr=0.6, - StopIgnores=50, # Auto stop on ignores-in-a-row starts to take effect + StopIgnores=25, # Auto stop on ignores-in-a-row starts to take effect # Miscs ResponseTime=3, # Decrease response time: 5 --> 3 @@ -333,7 +333,7 @@ MaxTrial=1000, MaxTime=90, - StopIgnores=50, + StopIgnores=25, # Miscs ResponseTime=1.0, diff --git a/code/aind_auto_train/curriculums/uncoupled_baiting_2p2.py b/code/aind_auto_train/curriculums/uncoupled_baiting_2p2.py index c8a7b00..c488a62 100644 --- a/code/aind_auto_train/curriculums/uncoupled_baiting_2p2.py +++ b/code/aind_auto_train/curriculums/uncoupled_baiting_2p2.py @@ -195,6 +195,8 @@ DelayMin=0.3, DelayMax=0.3, + StopIgnores=25, + # -- Within session automation -- # Miscs ResponseTime=3.0, # Decrease response time: 5 --> 3 @@ -318,7 +320,7 @@ MaxTrial=1000, MaxTime=90, - StopIgnores=20000, + StopIgnores=25, # Miscs ResponseTime=1.0, diff --git a/code/aind_auto_train/curriculums/uncoupled_no_baiting_2p2.py b/code/aind_auto_train/curriculums/uncoupled_no_baiting_2p2.py index 815c127..97df9d8 100644 --- a/code/aind_auto_train/curriculums/uncoupled_no_baiting_2p2.py +++ b/code/aind_auto_train/curriculums/uncoupled_no_baiting_2p2.py @@ -195,6 +195,8 @@ DelayMin=0.3, DelayMax=0.3, + StopIgnores=25, + # -- Within session automation -- # Miscs ResponseTime=3.0, # Decrease response time: 5 --> 3 @@ -316,7 +318,7 @@ MaxTrial=1000, MaxTime=90, - StopIgnores=20000, + StopIgnores=25, # Miscs ResponseTime=1.0, diff --git a/code/aind_auto_train/curriculums/uncoupled_no_baiting_2p2rwdDelay159.py b/code/aind_auto_train/curriculums/uncoupled_no_baiting_2p2rwdDelay159.py index 911d80a..fc6606c 100644 --- a/code/aind_auto_train/curriculums/uncoupled_no_baiting_2p2rwdDelay159.py +++ b/code/aind_auto_train/curriculums/uncoupled_no_baiting_2p2rwdDelay159.py @@ -195,6 +195,8 @@ # DelayMin=0.5, # DelayMax=0.5, + StopIgnores=25, + # -- Within session automation -- # Miscs ResponseTime=1.5, # Decrease response time: 5 --> 1.5 @@ -380,7 +382,7 @@ MaxTrial=1000, MaxTime=90, - StopIgnores=20000, + StopIgnores=25, # Miscs ResponseTime=1.5, From 141ece1dd2ebb1160fcdfeff00f6ed5c775b82e7 Mon Sep 17 00:00:00 2001 From: han-lab Date: Tue, 18 Jun 2024 16:42:43 -0700 Subject: [PATCH 3/5] feat: lower threshold for autowater --- code/aind_auto_train/curriculums/coupled_baiting_2p2.py | 8 ++++---- code/aind_auto_train/curriculums/uncoupled_baiting_2p2.py | 8 ++++---- .../curriculums/uncoupled_no_baiting_2p2.py | 8 ++++---- .../curriculums/uncoupled_no_baiting_2p2rwdDelay159.py | 8 ++++---- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/code/aind_auto_train/curriculums/coupled_baiting_2p2.py b/code/aind_auto_train/curriculums/coupled_baiting_2p2.py index e3dfcf7..5633287 100644 --- a/code/aind_auto_train/curriculums/coupled_baiting_2p2.py +++ b/code/aind_auto_train/curriculums/coupled_baiting_2p2.py @@ -187,8 +187,8 @@ # -- Within session automation -- # Decrease auto water: unrewarded 5 --> 10, ignored 5 --> 10 - Unrewarded=10, - Ignored=10, + Unrewarded=7, + Ignored=7, # Increase auto block switch threshold: 0.5 --> 0.6 SwitchThr=0.6, @@ -253,8 +253,8 @@ DelayBeta=0.0, # Decrease autowater number (almost turned off) - Unrewarded=15, - Ignored=15, + Unrewarded=10, + Ignored=10, # Miscs ResponseTime=2, # Decrease response time: 3 --> 2 diff --git a/code/aind_auto_train/curriculums/uncoupled_baiting_2p2.py b/code/aind_auto_train/curriculums/uncoupled_baiting_2p2.py index c488a62..8cf6735 100644 --- a/code/aind_auto_train/curriculums/uncoupled_baiting_2p2.py +++ b/code/aind_auto_train/curriculums/uncoupled_baiting_2p2.py @@ -181,8 +181,8 @@ RewardPairsN=1, # Decrease autowater - Unrewarded=10, - Ignored=10, + Unrewarded=7, + Ignored=7, # block length [10, 30, 10] --> [20, 35, 20] BlockMin=20, @@ -258,8 +258,8 @@ # Turn on auto water for the first day after switching to uncoupled task AutoReward=True, - Unrewarded=15, # almost turned off - Ignored=15, # almost turned off + Unrewarded=10, # almost turned off + Ignored=10, # almost turned off # Turn off auto block AdvancedBlockAuto=AdvancedBlockMode.OFF, # Turn off auto block diff --git a/code/aind_auto_train/curriculums/uncoupled_no_baiting_2p2.py b/code/aind_auto_train/curriculums/uncoupled_no_baiting_2p2.py index 97df9d8..2a6b187 100644 --- a/code/aind_auto_train/curriculums/uncoupled_no_baiting_2p2.py +++ b/code/aind_auto_train/curriculums/uncoupled_no_baiting_2p2.py @@ -180,8 +180,8 @@ RewardPairsN=1, # Decrease autowater - Unrewarded=10, - Ignored=10, + Unrewarded=7, + Ignored=7, # block length [10, 30, 10] --> [20, 35, 20] BlockMin=20, @@ -258,8 +258,8 @@ # Turn on auto water for the first day after switching to uncoupled task AutoReward=True, - Unrewarded=15, # almost turned off - Ignored=15, # almost turned off + Unrewarded=10, # almost turned off + Ignored=10, # almost turned off # Turn off auto block AdvancedBlockAuto=AdvancedBlockMode.OFF, # Turn off auto block diff --git a/code/aind_auto_train/curriculums/uncoupled_no_baiting_2p2rwdDelay159.py b/code/aind_auto_train/curriculums/uncoupled_no_baiting_2p2rwdDelay159.py index fc6606c..ab94f09 100644 --- a/code/aind_auto_train/curriculums/uncoupled_no_baiting_2p2rwdDelay159.py +++ b/code/aind_auto_train/curriculums/uncoupled_no_baiting_2p2rwdDelay159.py @@ -180,8 +180,8 @@ RewardPairsN=1, # Decrease autowater - Unrewarded=10, - Ignored=10, + Unrewarded=7, + Ignored=7, # block length [10, 30, 10] --> [20, 35, 20] BlockMin=20, @@ -322,8 +322,8 @@ # Turn on auto water for the first day after switching to uncoupled task AutoReward=True, - Unrewarded=15, # almost turned off - Ignored=15, # almost turned off + Unrewarded=10, # almost turned off + Ignored=10, # almost turned off # Turn off auto block AdvancedBlockAuto=AdvancedBlockMode.OFF, # Turn off auto block From f03ed1884e745a658235d1085a0618aafb715ffa Mon Sep 17 00:00:00 2001 From: han-lab Date: Tue, 18 Jun 2024 16:50:04 -0700 Subject: [PATCH 4/5] feat: update metadata --- code/aind_auto_train/curriculums/coupled_baiting_2p2.py | 4 ++-- code/aind_auto_train/curriculums/uncoupled_baiting_2p2.py | 4 ++-- code/aind_auto_train/curriculums/uncoupled_no_baiting_2p2.py | 4 ++-- .../curriculums/uncoupled_no_baiting_2p2rwdDelay159.py | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/code/aind_auto_train/curriculums/coupled_baiting_2p2.py b/code/aind_auto_train/curriculums/coupled_baiting_2p2.py index 5633287..b797d1e 100644 --- a/code/aind_auto_train/curriculums/coupled_baiting_2p2.py +++ b/code/aind_auto_train/curriculums/coupled_baiting_2p2.py @@ -21,8 +21,8 @@ setup_logging() curriculum_name = Task.C1B1 -curriculum_version = "2.1" -curriculum_description = '''2024-05-09 decrease delay period as we now use much longer early lick punishment''' +curriculum_version = "2.2" +curriculum_description = '''2024-06-18 less rollback from FINAL; more AutoWater; AutoIgnore=25''' task_url = "https://github.com/AllenNeuralDynamics/dynamic-foraging-task" task_schema_version = "1.1.0" diff --git a/code/aind_auto_train/curriculums/uncoupled_baiting_2p2.py b/code/aind_auto_train/curriculums/uncoupled_baiting_2p2.py index 8cf6735..396a9d1 100644 --- a/code/aind_auto_train/curriculums/uncoupled_baiting_2p2.py +++ b/code/aind_auto_train/curriculums/uncoupled_baiting_2p2.py @@ -24,8 +24,8 @@ # Note this could be any string, not necessarily one of the Task enums curriculum_name = "Uncoupled Baiting" -curriculum_version = "2.1" -curriculum_description = '''2024-05-09 decrease delay period as we now use much longer early lick punishment''' +curriculum_version = "2.2" +curriculum_description = '''2024-06-18 less rollback from FINAL; more AutoWater; AutoIgnore=25''' task_url = "https://github.com/AllenNeuralDynamics/dynamic-foraging-task" task_schema_version = "1.1.0" diff --git a/code/aind_auto_train/curriculums/uncoupled_no_baiting_2p2.py b/code/aind_auto_train/curriculums/uncoupled_no_baiting_2p2.py index 2a6b187..ea1004a 100644 --- a/code/aind_auto_train/curriculums/uncoupled_no_baiting_2p2.py +++ b/code/aind_auto_train/curriculums/uncoupled_no_baiting_2p2.py @@ -23,8 +23,8 @@ # Note this could be any string, not necessarily one of the Task enums curriculum_name = Task.C0B0 -curriculum_version = "2.1" -curriculum_description = '''2024-05-09 decrease delay period as we now use much longer early lick punishment''' +curriculum_version = "2.2" +curriculum_description = '''2024-06-18 less rollback from FINAL; more AutoWater; AutoIgnore=25''' task_url = "https://github.com/AllenNeuralDynamics/dynamic-foraging-task" task_schema_version = "1.1.0" diff --git a/code/aind_auto_train/curriculums/uncoupled_no_baiting_2p2rwdDelay159.py b/code/aind_auto_train/curriculums/uncoupled_no_baiting_2p2rwdDelay159.py index ab94f09..d0bf6fe 100644 --- a/code/aind_auto_train/curriculums/uncoupled_no_baiting_2p2rwdDelay159.py +++ b/code/aind_auto_train/curriculums/uncoupled_no_baiting_2p2rwdDelay159.py @@ -23,8 +23,8 @@ # Note this could be any string, not necessarily one of the Task enums curriculum_name = Task.C0B0 -curriculum_version = "2.1rwdDelay159" -curriculum_description = '''2024-05-09 added reward delay; decrease no-lick delay in early training; use [0.1, 0.5, 0.9]''' +curriculum_version = "2.2rwdDelay159" +curriculum_description = '''2024-06-18 less rollback from FINAL; more AutoWater; AutoIgnore=25''' task_url = "https://github.com/AllenNeuralDynamics/dynamic-foraging-task" task_schema_version = "1.1.0" From 8035174cfab69f928be57844d7cb00b8c34b5dd3 Mon Sep 17 00:00:00 2001 From: han-lab Date: Tue, 18 Jun 2024 16:59:26 -0700 Subject: [PATCH 5/5] feat: make it harder to rollback from FINAL --- code/aind_auto_train/curriculums/coupled_baiting_2p2.py | 6 +++--- code/aind_auto_train/curriculums/uncoupled_baiting_2p2.py | 6 +++--- .../aind_auto_train/curriculums/uncoupled_no_baiting_2p2.py | 6 +++--- .../curriculums/uncoupled_no_baiting_2p2rwdDelay159.py | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/code/aind_auto_train/curriculums/coupled_baiting_2p2.py b/code/aind_auto_train/curriculums/coupled_baiting_2p2.py index b797d1e..00f35a8 100644 --- a/code/aind_auto_train/curriculums/coupled_baiting_2p2.py +++ b/code/aind_auto_train/curriculums/coupled_baiting_2p2.py @@ -366,11 +366,11 @@ TransitionRule( decision=Decision.ROLLBACK, to_stage=TrainingStage.STAGE_3, - condition_description="For recent 2 sessions, mean finished trials < 350 or efficiency < 0.65", + condition_description="For recent 5 sessions, mean finished trials < 300 or efficiency < 0.60", condition="""lambda metrics: - np.mean(metrics.finished_trials[-2:]) < 350 + np.mean(metrics.finished_trials[-5:]) < 300 or - np.mean(metrics.foraging_efficiency[-2:]) < 0.65 + np.mean(metrics.foraging_efficiency[-5:]) < 0.60 """, ), ] diff --git a/code/aind_auto_train/curriculums/uncoupled_baiting_2p2.py b/code/aind_auto_train/curriculums/uncoupled_baiting_2p2.py index 396a9d1..bc548df 100644 --- a/code/aind_auto_train/curriculums/uncoupled_baiting_2p2.py +++ b/code/aind_auto_train/curriculums/uncoupled_baiting_2p2.py @@ -352,11 +352,11 @@ TransitionRule( decision=Decision.ROLLBACK, to_stage=TrainingStage.STAGE_3, # Back to C0B0 with auto water - condition_description="For recent 2 sessions, mean finished trials < 300 or efficiency < 0.6", + condition_description="For recent 5 sessions, mean finished trials < 300 or efficiency < 0.6", condition="""lambda metrics: - np.mean(metrics.finished_trials[-2:]) < 300 + np.mean(metrics.finished_trials[-5:]) < 300 or - np.mean(metrics.foraging_efficiency[-2:]) < 0.6 + np.mean(metrics.foraging_efficiency[-5:]) < 0.60 """, ), ] diff --git a/code/aind_auto_train/curriculums/uncoupled_no_baiting_2p2.py b/code/aind_auto_train/curriculums/uncoupled_no_baiting_2p2.py index ea1004a..cfa87a4 100644 --- a/code/aind_auto_train/curriculums/uncoupled_no_baiting_2p2.py +++ b/code/aind_auto_train/curriculums/uncoupled_no_baiting_2p2.py @@ -350,11 +350,11 @@ TransitionRule( decision=Decision.ROLLBACK, to_stage=TrainingStage.STAGE_3, # Back to C0B0 with auto water - condition_description="For recent 2 sessions, mean finished trials < 300 or efficiency < 0.6", + condition_description="For recent 5 sessions, mean finished trials < 300 or efficiency < 0.6", condition="""lambda metrics: - np.mean(metrics.finished_trials[-2:]) < 300 + np.mean(metrics.finished_trials[-5:]) < 300 or - np.mean(metrics.foraging_efficiency[-2:]) < 0.6 + np.mean(metrics.foraging_efficiency[-5:]) < 0.60 """, ), ] diff --git a/code/aind_auto_train/curriculums/uncoupled_no_baiting_2p2rwdDelay159.py b/code/aind_auto_train/curriculums/uncoupled_no_baiting_2p2rwdDelay159.py index d0bf6fe..64cdf73 100644 --- a/code/aind_auto_train/curriculums/uncoupled_no_baiting_2p2rwdDelay159.py +++ b/code/aind_auto_train/curriculums/uncoupled_no_baiting_2p2rwdDelay159.py @@ -414,11 +414,11 @@ TransitionRule( decision=Decision.ROLLBACK, to_stage=TrainingStage.STAGE_4, # Back to C0B0 with auto water - condition_description="For recent 2 sessions, mean finished trials < 300 or efficiency < 0.6", + condition_description="For recent 5 sessions, mean finished trials < 300 or efficiency < 0.6", condition="""lambda metrics: - np.mean(metrics.finished_trials[-2:]) < 300 + np.mean(metrics.finished_trials[-5:]) < 300 or - np.mean(metrics.foraging_efficiency[-2:]) < 0.6 + np.mean(metrics.foraging_efficiency[-5:]) < 0.60 """, ), ]