mxbiflow 0.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. mxbiflow/__init__.py +3 -0
  2. mxbiflow/assets/__init__.py +5 -0
  3. mxbiflow/assets/clicker.wav +0 -0
  4. mxbiflow/config_store.py +68 -0
  5. mxbiflow/data_logger.py +114 -0
  6. mxbiflow/default/__init__.py +4 -0
  7. mxbiflow/default/idle/assets/apple_v1.png +0 -0
  8. mxbiflow/default/idle/idle.py +57 -0
  9. mxbiflow/detector_bridge.py +87 -0
  10. mxbiflow/game.py +84 -0
  11. mxbiflow/infra/eventbus.py +31 -0
  12. mxbiflow/main.py +106 -0
  13. mxbiflow/models/animal.py +130 -0
  14. mxbiflow/models/reward.py +7 -0
  15. mxbiflow/models/session.py +145 -0
  16. mxbiflow/mxbiflow.py +43 -0
  17. mxbiflow/path.py +41 -0
  18. mxbiflow/scene/__init__.py +8 -0
  19. mxbiflow/scene/scene_manager.py +64 -0
  20. mxbiflow/scene/scene_protocol.py +22 -0
  21. mxbiflow/scheduler.py +90 -0
  22. mxbiflow/tasks/GNGSiD/models.py +70 -0
  23. mxbiflow/tasks/GNGSiD/stages/detect_stage/config.json +116 -0
  24. mxbiflow/tasks/GNGSiD/stages/detect_stage/detect_stage.py +161 -0
  25. mxbiflow/tasks/GNGSiD/stages/detect_stage/detect_stage_models.py +65 -0
  26. mxbiflow/tasks/GNGSiD/stages/discriminate_stage/config.json +70 -0
  27. mxbiflow/tasks/GNGSiD/stages/discriminate_stage/discriminate_stage.py +173 -0
  28. mxbiflow/tasks/GNGSiD/stages/discriminate_stage/discriminate_stage_models.py +80 -0
  29. mxbiflow/tasks/GNGSiD/stages/size_reduction_stage/config.json +83 -0
  30. mxbiflow/tasks/GNGSiD/stages/size_reduction_stage/size_reduction_models.py +58 -0
  31. mxbiflow/tasks/GNGSiD/stages/size_reduction_stage/size_reduction_stage.py +149 -0
  32. mxbiflow/tasks/GNGSiD/tasks/artifacts.py +13 -0
  33. mxbiflow/tasks/GNGSiD/tasks/detect/models.py +21 -0
  34. mxbiflow/tasks/GNGSiD/tasks/detect/scene.py +271 -0
  35. mxbiflow/tasks/GNGSiD/tasks/discriminate/discriminate_models.py +31 -0
  36. mxbiflow/tasks/GNGSiD/tasks/discriminate/discriminate_scene.py +336 -0
  37. mxbiflow/tasks/GNGSiD/tasks/touch/touch_models.py +17 -0
  38. mxbiflow/tasks/GNGSiD/tasks/touch/touch_scene.py +256 -0
  39. mxbiflow/tasks/GNGSiD/tasks/utils/targets.py +57 -0
  40. mxbiflow/tasks/cross_modal/bundle_dir.py +553 -0
  41. mxbiflow/tasks/cross_modal/config.py +41 -0
  42. mxbiflow/tasks/cross_modal/media.py +61 -0
  43. mxbiflow/tasks/cross_modal/models.py +57 -0
  44. mxbiflow/tasks/cross_modal/scene.py +252 -0
  45. mxbiflow/tasks/cross_modal/stage.py +218 -0
  46. mxbiflow/tasks/cross_modal/trial_io.py +23 -0
  47. mxbiflow/tasks/cross_modal/trial_schema.py +113 -0
  48. mxbiflow/tasks/default/error_task/error_scene.py +53 -0
  49. mxbiflow/tasks/default/idle_task/assets/apple_v1.png +0 -0
  50. mxbiflow/tasks/default/idle_task/idle_scene.py +85 -0
  51. mxbiflow/tasks/default/initial_habituation_training/README.md +188 -0
  52. mxbiflow/tasks/default/initial_habituation_training/stages/config.csv +7 -0
  53. mxbiflow/tasks/default/initial_habituation_training/stages/config.json +67 -0
  54. mxbiflow/tasks/default/initial_habituation_training/stages/initial_habituation_training_stage.py +172 -0
  55. mxbiflow/tasks/default/initial_habituation_training/stages/models.py +56 -0
  56. mxbiflow/tasks/default/initial_habituation_training/tasks/stay_to_reward/stay_to_reward.py +244 -0
  57. mxbiflow/tasks/default/initial_habituation_training/tasks/stay_to_reward/stay_to_reward_models.py +50 -0
  58. mxbiflow/tasks/task_protocol.py +26 -0
  59. mxbiflow/tasks/task_table.py +29 -0
  60. mxbiflow/tasks/two_alternative_choice/assets/starter.py +27 -0
  61. mxbiflow/tasks/two_alternative_choice/models.py +68 -0
  62. mxbiflow/tasks/two_alternative_choice/stages/size_reduction_stage/config.json +118 -0
  63. mxbiflow/tasks/two_alternative_choice/stages/size_reduction_stage/size_reduction_models.py +41 -0
  64. mxbiflow/tasks/two_alternative_choice/stages/size_reduction_stage/size_reduction_stage.py +122 -0
  65. mxbiflow/tasks/two_alternative_choice/tasks/touch/touch_models.py +19 -0
  66. mxbiflow/tasks/two_alternative_choice/tasks/touch/touch_scene.py +249 -0
  67. mxbiflow/timer/__init__.py +3 -0
  68. mxbiflow/timer/frame_timer.py +47 -0
  69. mxbiflow/timer/realtime_timer.py +0 -0
  70. mxbiflow/tmp_email.py +13 -0
  71. mxbiflow/ui/components/animal.py +87 -0
  72. mxbiflow/ui/components/baseconfig.py +68 -0
  73. mxbiflow/ui/components/card.py +18 -0
  74. mxbiflow/ui/components/device_card/__init__.py +17 -0
  75. mxbiflow/ui/components/device_card/detector/beambreak_detector_card.py +29 -0
  76. mxbiflow/ui/components/device_card/detector/fusion_detector.py +45 -0
  77. mxbiflow/ui/components/device_card/detector/mock_detector_card.py +20 -0
  78. mxbiflow/ui/components/device_card/detector/rfid_detector.py +40 -0
  79. mxbiflow/ui/components/device_card/device_card.py +67 -0
  80. mxbiflow/ui/components/device_card/rewarder/mock_rewarder_card.py +20 -0
  81. mxbiflow/ui/components/device_card/rewarder/rpi_gpio_rewarder.py +33 -0
  82. mxbiflow/ui/components/devices.py +183 -0
  83. mxbiflow/ui/components/dialog/__init__.py +3 -0
  84. mxbiflow/ui/components/dialog/add_devices_dialog.py +64 -0
  85. mxbiflow/ui/components/experiment_groups.py +122 -0
  86. mxbiflow/ui/experiment_panel.py +91 -0
  87. mxbiflow/ui/mxbi_panel.py +152 -0
  88. mxbiflow/utils/logger.py +19 -0
  89. mxbiflow/utils/serial.py +10 -0
  90. mxbiflow-0.1.1.dist-info/METADATA +168 -0
  91. mxbiflow-0.1.1.dist-info/RECORD +93 -0
  92. mxbiflow-0.1.1.dist-info/WHEEL +4 -0
  93. mxbiflow-0.1.1.dist-info/entry_points.txt +4 -0
@@ -0,0 +1,161 @@
1
+ from random import choice, choices, randint
2
+ from typing import TYPE_CHECKING, Final
3
+
4
+ from mxbi.data_logger import DataLogger, DataLoggerType
5
+ from mxbi.models.animal import ScheduleCondition
6
+ from mxbi.tasks.GNGSiD.models import PersistentData, Result
7
+ from mxbi.tasks.GNGSiD.stages.detect_stage.detect_stage_models import (
8
+ DetectStageConfig,
9
+ config,
10
+ )
11
+ from mxbi.tasks.GNGSiD.tasks.detect.models import TrialConfig
12
+ from mxbi.tasks.GNGSiD.tasks.detect.scene import GNGSiDDetectScene
13
+ from mxbi.utils.logger import logger
14
+
15
+ if TYPE_CHECKING:
16
+ from mxbi.models.animal import AnimalState
17
+ from mxbi.models.session import SessionState
18
+ from mxbi.models.task import Feedback
19
+ from mxbi.theater import Theater
20
+
21
+
22
+ _presistent_data: dict[str, PersistentData] = {}
23
+
24
+
25
+ class GNGSiDDetectStage:
26
+ STAGE_NAME: Final[str] = "GNGSiD_DETECT_STAGE"
27
+
28
+ def __init__(
29
+ self,
30
+ theater: "Theater",
31
+ session_state: "SessionState",
32
+ animal_state: "AnimalState",
33
+ ) -> None:
34
+ self._theater = theater
35
+ self._session_state = session_state
36
+ self._animal_state = animal_state
37
+
38
+ self._stage_config = self._load_stage_config(animal_state.name)
39
+
40
+ _fixed_config = self._stage_config.params
41
+ _levels_config = self._stage_config.levels_table[animal_state.level]
42
+
43
+ _is_go = choices(
44
+ [True, False],
45
+ weights=[_levels_config.go_task_prob, _levels_config.nogo_task_prob],
46
+ )[0]
47
+
48
+ _stimulus_duration = randint(
49
+ _levels_config.min_stimulus_duration,
50
+ _levels_config.max_stimulus_duration,
51
+ )
52
+
53
+ _master_amp, _digital_amp = self._prepare_stimulus_intensity(
54
+ self._animal_state.name, _fixed_config.stimulus_freq
55
+ )
56
+
57
+ _config = TrialConfig(
58
+ level=_levels_config.level,
59
+ stimulation_size=_fixed_config.stimulation_size,
60
+ stimulus_duration=_stimulus_duration,
61
+ time_out=_fixed_config.time_out,
62
+ inter_trial_interval=_fixed_config.inter_trial_interval,
63
+ reward_duration=_fixed_config.reward_duration,
64
+ reward_delay=_fixed_config.reward_delay,
65
+ go=_is_go,
66
+ visual_stimulus_delay=_fixed_config.visual_stimulus_delay,
67
+ stimulus_freq=_fixed_config.stimulus_freq,
68
+ stimulus_freq_duration=_fixed_config.stimulus_freq_duration,
69
+ stimulus_freq_master_amp=_master_amp,
70
+ stimulus_freq_digital_amp=_digital_amp,
71
+ stimulus_interval=_fixed_config.stimulus_interval,
72
+ )
73
+
74
+ self._presistent_data = _presistent_data.get(self._animal_state.name)
75
+
76
+ if self._presistent_data is None:
77
+ self._presistent_data = PersistentData(
78
+ rewards=0,
79
+ correct=0,
80
+ incorrect=0,
81
+ timeout=0,
82
+ )
83
+ _presistent_data[self._animal_state.name] = self._presistent_data
84
+
85
+ self._task = GNGSiDDetectScene(
86
+ theater,
87
+ session_state.session_config,
88
+ animal_state,
89
+ session_state.session_config.screen_type,
90
+ _config,
91
+ self._presistent_data,
92
+ )
93
+
94
+ self._data_logger = DataLogger(
95
+ self._session_state,
96
+ self._animal_state.name,
97
+ self.STAGE_NAME,
98
+ DataLoggerType.JSONL,
99
+ )
100
+
101
+ def start(self) -> "Feedback":
102
+ trial_data = self._task.start()
103
+ self._data_logger.save(trial_data.model_dump())
104
+
105
+ feedback = self._handle_result(trial_data.result)
106
+ logger.debug(
107
+ f"{self.STAGE_NAME}: "
108
+ f"session_id={self._session_state.session_id}, "
109
+ f"animal_name={self._animal_state.name}, "
110
+ f"animal_level={self._animal_state.level}, "
111
+ f"state_name={self.STAGE_NAME}, "
112
+ f"result={trial_data}, "
113
+ f"feedback={feedback}"
114
+ )
115
+
116
+ return feedback
117
+
118
+ def _load_stage_config(self, monkey: str) -> DetectStageConfig:
119
+ stage_config = config.root.get(monkey) or config.root.get("default")
120
+ if stage_config is None:
121
+ raise ValueError("No default stage config found")
122
+ return stage_config
123
+
124
+ def _handle_result(self, result: "Result") -> "Feedback":
125
+ feedback = False
126
+ match result:
127
+ case Result.CORRECT:
128
+ _presistent_data[self._animal_state.name].correct += 1
129
+ feedback = True
130
+ case Result.INCORRECT:
131
+ _presistent_data[self._animal_state.name].incorrect += 1
132
+ feedback = False
133
+ case Result.TIMEOUT:
134
+ _presistent_data[self._animal_state.name].timeout += 1
135
+ feedback = False
136
+ case Result.CANCEL:
137
+ feedback = False
138
+
139
+ return feedback
140
+
141
+ def quit(self) -> None:
142
+ self._task.cancle()
143
+
144
+ def on_idle(self) -> None:
145
+ self._task.cancle()
146
+
147
+ def on_return(self) -> None:
148
+ self._task.cancle()
149
+
150
+ @property
151
+ def condition(self) -> "ScheduleCondition | None":
152
+ return self._stage_config.condition
153
+
154
+ def _prepare_stimulus_intensity(self, monkey: str, frequency: int):
155
+ bt = choice(([[10, 30], [50, 70]])) if monkey == "wolfgang" else []
156
+ at = [80, 80, 80] if monkey == "wolfgang" else [80, 80, 80]
157
+ intensity_options = at * 10 + bt
158
+
159
+ stimulus_intensity = choice(intensity_options)
160
+
161
+ return self._theater.acontroller.get_amp_value(frequency, stimulus_intensity)
@@ -0,0 +1,65 @@
1
+ from pathlib import Path
2
+
3
+ from mxbi.config import Configure
4
+ from mxbi.models.animal import ScheduleCondition
5
+ from mxbi.tasks.GNGSiD.models import LevelID, MonkeyName
6
+ from pydantic import BaseModel, ConfigDict, RootModel
7
+
8
+ CONFIG_PATH = Path(__file__).parent / "config.json"
9
+
10
+
11
+ class DetectStageParams(BaseModel):
12
+ model_config = ConfigDict(frozen=True)
13
+
14
+ # visual stimulation config
15
+ stimulation_size: int
16
+ visual_stimulus_delay: int
17
+
18
+ # audio stimulation config
19
+ stimulus_freq: int
20
+ stimulus_freq_duration: int
21
+ stimulus_interval: int
22
+
23
+ # trial lifecycle
24
+ time_out: int
25
+ inter_trial_interval: int
26
+
27
+ # reward config
28
+ reward_duration: int
29
+ reward_delay: int
30
+
31
+
32
+ class DetectStageLeveledParams(BaseModel):
33
+ model_config = ConfigDict(frozen=True)
34
+
35
+ level: int
36
+ min_stimulus_duration: int
37
+ max_stimulus_duration: int
38
+
39
+ go_task_prob: float
40
+ nogo_task_prob: float
41
+
42
+
43
+ class DetectStageConfig(BaseModel):
44
+ model_config = ConfigDict(frozen=True)
45
+
46
+ condition: ScheduleCondition
47
+
48
+ params: DetectStageParams
49
+ levels_table: dict[LevelID, DetectStageLeveledParams]
50
+
51
+
52
+ class DetectStageConfigs(RootModel):
53
+ model_config = ConfigDict(frozen=True)
54
+
55
+ root: dict[MonkeyName, DetectStageConfig]
56
+
57
+
58
+ def load_config() -> DetectStageConfigs:
59
+ configs = Configure(CONFIG_PATH, DetectStageConfigs).value
60
+ for config in configs.root.values():
61
+ config.condition.level_count = len(config.levels_table)
62
+ return configs
63
+
64
+
65
+ config = load_config()
@@ -0,0 +1,70 @@
1
+ {
2
+ "default": {
3
+ "condition": {
4
+ "config": {
5
+ "evaluation_interval": 20,
6
+ "difficulty_increase_threshold": 0.8,
7
+ "difficulty_decrease_threshold": 0.45
8
+ }
9
+ },
10
+ "params": {
11
+ "level": 0,
12
+ "stimulation_size": 250,
13
+ "visual_stimulus_delay": 500,
14
+ "min_stimulus_duration": 2500,
15
+ "max_stimulus_duration": 6500,
16
+ "extra_response_time": 1000,
17
+ "stimulus_configs": [
18
+ {
19
+ "stimulus_freq_high": 2640,
20
+ "stimulus_freq_high_duration": 100,
21
+ "stimulus_freq_low": 1760,
22
+ "stimulus_freq_low_duration": 100
23
+ },
24
+ {
25
+ "stimulus_freq_high": 5920,
26
+ "stimulus_freq_high_duration": 100,
27
+ "stimulus_freq_low": 3950,
28
+ "stimulus_freq_low_duration": 100
29
+ },
30
+ {
31
+ "stimulus_freq_high": 7000,
32
+ "stimulus_freq_high_duration": 100,
33
+ "stimulus_freq_low": 4700,
34
+ "stimulus_freq_low_duration": 100
35
+ }
36
+ ],
37
+ "stimulus_interval": 200,
38
+ "time_out": 10000,
39
+ "inter_trial_interval": 2000,
40
+ "reward_duration": 1000,
41
+ "medium_reward_duration": 800,
42
+ "medium_reward_threshold": 2000,
43
+ "low_reward_duration": 500,
44
+ "reward_delay": 500,
45
+ "attention_duration": 2800
46
+ },
47
+ "levels_table": {
48
+ "0": {
49
+ "level": 0,
50
+ "stimulus_trial_prob": 0.2,
51
+ "nostimulus_trial_prob": 0.8
52
+ },
53
+ "1": {
54
+ "level": 1,
55
+ "stimulus_trial_prob": 0.3,
56
+ "nostimulus_trial_prob": 0.7
57
+ },
58
+ "2": {
59
+ "level": 2,
60
+ "stimulus_trial_prob": 0.4,
61
+ "nostimulus_trial_prob": 0.6
62
+ },
63
+ "3": {
64
+ "level": 3,
65
+ "stimulus_trial_prob": 0.5,
66
+ "nostimulus_trial_prob": 0.5
67
+ }
68
+ }
69
+ }
70
+ }
@@ -0,0 +1,173 @@
1
+ from random import choice, choices, randint
2
+ from typing import TYPE_CHECKING, Final
3
+
4
+ from mxbi.data_logger import DataLogger, DataLoggerType
5
+ from mxbi.models.animal import ScheduleCondition
6
+ from mxbi.tasks.GNGSiD.models import PersistentData, Result
7
+ from mxbi.tasks.GNGSiD.stages.discriminate_stage.discriminate_stage_models import (
8
+ DiscriminateStageConfig,
9
+ config,
10
+ )
11
+ from mxbi.tasks.GNGSiD.tasks.discriminate.discriminate_models import TrialConfig
12
+ from mxbi.tasks.GNGSiD.tasks.discriminate.discriminate_scene import (
13
+ GNGSiDDiscriminateScene,
14
+ )
15
+ from mxbi.utils.logger import logger
16
+
17
+ if TYPE_CHECKING:
18
+ from mxbi.models.animal import AnimalState
19
+ from mxbi.models.session import SessionState
20
+ from mxbi.models.task import Feedback
21
+ from mxbi.theater import Theater
22
+
23
+ _presistent_data: dict[str, PersistentData] = {}
24
+
25
+
26
+ class GNGSiDDiscriminateStage:
27
+ STAGE_NAME: Final[str] = "GNGSiD_DISCRIMINATE_STAGE"
28
+
29
+ def __init__(
30
+ self,
31
+ theater: "Theater",
32
+ session_state: "SessionState",
33
+ animal_state: "AnimalState",
34
+ ) -> None:
35
+ self._theater = theater
36
+ self._session_state = session_state
37
+ self._animal_state = animal_state
38
+
39
+ self._stage_config = self._load_stage_config(animal_state.name)
40
+
41
+ _fixed_config = self._stage_config.params
42
+ _levels_config = self._stage_config.levels_table[animal_state.level]
43
+
44
+ _stimulus_config = choice(_fixed_config.stimulus_configs)
45
+ _stimulus_duration = randint(
46
+ _fixed_config.min_stimulus_duration, _fixed_config.max_stimulus_duration
47
+ )
48
+
49
+ _is_stimulus_trial = choices(
50
+ [True, False],
51
+ weights=[
52
+ _levels_config.stimulus_trial_prob,
53
+ _levels_config.nostimulus_trial_prob,
54
+ ],
55
+ )[0]
56
+
57
+ _high_master_amp, _high_digital_amp = self._prepare_stimulus_intensity(
58
+ animal_state.name, _stimulus_config.stimulus_freq_high
59
+ )
60
+ _low_master_amp, _low_digital_amp = self._prepare_stimulus_intensity(
61
+ animal_state.name, _stimulus_config.stimulus_freq_low
62
+ )
63
+
64
+ _config = TrialConfig(
65
+ level=_levels_config.level,
66
+ stimulation_size=_fixed_config.stimulation_size,
67
+ stimulus_duration=_stimulus_duration,
68
+ time_out=_fixed_config.time_out,
69
+ inter_trial_interval=_fixed_config.inter_trial_interval,
70
+ reward_duration=_fixed_config.reward_duration,
71
+ reward_delay=_fixed_config.reward_delay,
72
+ is_stimulus_trial=_is_stimulus_trial,
73
+ visual_stimulus_delay=_fixed_config.visual_stimulus_delay,
74
+ medium_reward_duration=_fixed_config.medium_reward_duration,
75
+ medium_reward_threshold=_fixed_config.medium_reward_threshold,
76
+ low_reward_duration=_fixed_config.low_reward_duration,
77
+ attention_duration=_fixed_config.attention_duration,
78
+ stimulus_freq_low=_stimulus_config.stimulus_freq_low,
79
+ stimulus_freq_low_duration=_stimulus_config.stimulus_freq_low_duration,
80
+ stimulus_freq_low_master_amp=_low_master_amp,
81
+ stimulus_freq_low_digital_amp=_low_digital_amp,
82
+ stimulus_freq_high=_stimulus_config.stimulus_freq_high,
83
+ stimulus_freq_high_duration=_stimulus_config.stimulus_freq_high_duration,
84
+ stimulus_freq_high_master_amp=_high_master_amp,
85
+ stimulus_freq_high_digital_amp=_high_digital_amp,
86
+ stimulus_interval=_fixed_config.stimulus_interval,
87
+ extra_response_time=_fixed_config.extra_response_time,
88
+ )
89
+
90
+ self._presistent_data = _presistent_data.get(self._animal_state.name)
91
+
92
+ if self._presistent_data is None:
93
+ self._presistent_data = PersistentData(
94
+ rewards=0,
95
+ correct=0,
96
+ incorrect=0,
97
+ timeout=0,
98
+ )
99
+ _presistent_data[self._animal_state.name] = self._presistent_data
100
+
101
+ self._task = GNGSiDDiscriminateScene(
102
+ theater,
103
+ session_state.session_config,
104
+ animal_state,
105
+ session_state.session_config.screen_type,
106
+ _config,
107
+ self._presistent_data,
108
+ )
109
+
110
+ self._data_logger = DataLogger(
111
+ self._session_state,
112
+ self._animal_state.name,
113
+ self.STAGE_NAME,
114
+ DataLoggerType.JSONL,
115
+ )
116
+
117
+ def start(self) -> "Feedback":
118
+ trial_data = self._task.start()
119
+ self._data_logger.save(trial_data.model_dump())
120
+
121
+ feedback = self._handle_result(trial_data.result)
122
+ logger.debug(
123
+ f"{self.STAGE_NAME}: "
124
+ f"session_id={self._session_state.session_id}, "
125
+ f"animal_name={self._animal_state.name}, "
126
+ f"animal_level={self._animal_state.level}, "
127
+ f"state_name={self.STAGE_NAME}, "
128
+ f"result={trial_data}, "
129
+ f"feedback={feedback}"
130
+ )
131
+
132
+ return feedback
133
+
134
+ def _load_stage_config(self, monkey: str) -> DiscriminateStageConfig:
135
+ stage_config = config.root.get(monkey) or config.root.get("default")
136
+ if stage_config is None:
137
+ raise ValueError("No default stage config found")
138
+ return stage_config
139
+
140
+ def _handle_result(self, result: "Result") -> "Feedback":
141
+ feedback = False
142
+ match result:
143
+ case Result.CORRECT:
144
+ _presistent_data[self._animal_state.name].correct += 1
145
+ feedback = True
146
+ case Result.INCORRECT:
147
+ _presistent_data[self._animal_state.name].incorrect += 1
148
+ feedback = False
149
+ case Result.TIMEOUT:
150
+ _presistent_data[self._animal_state.name].timeout += 1
151
+ feedback = False
152
+ case Result.CANCEL:
153
+ feedback = False
154
+
155
+ return feedback
156
+
157
+ def quit(self) -> None:
158
+ self._task.cancle()
159
+
160
+ def on_idle(self) -> None:
161
+ self._task.cancle()
162
+
163
+ def on_return(self) -> None:
164
+ self._task.cancle()
165
+
166
+ @property
167
+ def condition(self) -> "ScheduleCondition | None":
168
+ return self._stage_config.condition
169
+
170
+ def _prepare_stimulus_intensity(self, monkey: str, frequency: int):
171
+ stimulus_intensity = choice([55, 60, 65, 70, 75])
172
+
173
+ return self._theater.acontroller.get_amp_value(frequency, stimulus_intensity)
@@ -0,0 +1,80 @@
1
+ from pathlib import Path
2
+ from typing import Dict, List
3
+
4
+ from mxbi.config import Configure
5
+ from mxbi.models.animal import ScheduleCondition
6
+ from mxbi.tasks.GNGSiD.models import LevelID, MonkeyName
7
+ from pydantic import BaseModel, ConfigDict, RootModel
8
+
9
+ CONFIG_PATH: Path = Path(__file__).parent / "config.json"
10
+
11
+
12
+ class StimulusConfig(BaseModel):
13
+ stimulus_freq_high: int
14
+ stimulus_freq_high_duration: int
15
+ stimulus_freq_low: int
16
+ stimulus_freq_low_duration: int
17
+
18
+
19
+ class DiscriminateStageParams(BaseModel):
20
+ model_config = ConfigDict(frozen=True)
21
+
22
+ # visual stimulation config
23
+ stimulation_size: int
24
+ visual_stimulus_delay: int
25
+
26
+ # dynamic reward config
27
+ medium_reward_duration: int
28
+ medium_reward_threshold: int
29
+ low_reward_duration: int
30
+
31
+ # audio stimulation config
32
+ min_stimulus_duration: int
33
+ max_stimulus_duration: int
34
+ stimulus_configs: List[StimulusConfig]
35
+ stimulus_interval: int
36
+ extra_response_time: int
37
+
38
+ # trial lifecycle
39
+ time_out: int
40
+ inter_trial_interval: int
41
+
42
+ # reward config
43
+ reward_duration: int
44
+ reward_delay: int
45
+
46
+ attention_duration: int
47
+
48
+
49
+ class DiscriminateStageLeveledParams(BaseModel):
50
+ model_config = ConfigDict(frozen=True)
51
+
52
+ level: int
53
+
54
+ stimulus_trial_prob: float
55
+ nostimulus_trial_prob: float
56
+
57
+
58
+ class DiscriminateStageConfig(BaseModel):
59
+ model_config = ConfigDict(frozen=True)
60
+
61
+ condition: ScheduleCondition
62
+
63
+ params: DiscriminateStageParams
64
+ levels_table: Dict[LevelID, DiscriminateStageLeveledParams]
65
+
66
+
67
+ class DiscriminateStageConfigs(RootModel):
68
+ model_config = ConfigDict(frozen=True)
69
+
70
+ root: Dict[MonkeyName, DiscriminateStageConfig]
71
+
72
+
73
+ def load_config() -> DiscriminateStageConfigs:
74
+ configs = Configure(CONFIG_PATH, DiscriminateStageConfigs).value
75
+ for config in configs.root.values():
76
+ config.condition.level_count = len(config.levels_table)
77
+ return configs
78
+
79
+
80
+ config = load_config()
@@ -0,0 +1,83 @@
1
+ {
2
+ "default": {
3
+ "condition": {
4
+ "config": {
5
+ "evaluation_interval": 20,
6
+ "difficulty_increase_threshold": 0.8,
7
+ "difficulty_decrease_threshold": 0.45,
8
+ "next_task": "gngsid_detect_stage"
9
+ }
10
+ },
11
+ "params": {
12
+ "stimulus_duration": 1000,
13
+ "stimulus_freq": 2000,
14
+ "stimulus_freq_duration": 100,
15
+ "stimulus_interval": 130,
16
+ "time_out": 10000,
17
+ "inter_trial_interval": 2000,
18
+ "reward_duration": 1000
19
+ },
20
+ "levels_table": {
21
+ "0": {
22
+ "level": 0,
23
+ "stimulation_size": 430,
24
+ "reward_delay": 0
25
+ },
26
+ "1": {
27
+ "level": 1,
28
+ "stimulation_size": 420,
29
+ "reward_delay": 0
30
+ },
31
+ "2": {
32
+ "level": 2,
33
+ "stimulation_size": 400,
34
+ "reward_delay": 100
35
+ },
36
+ "3": {
37
+ "level": 3,
38
+ "stimulation_size": 380,
39
+ "reward_delay": 200
40
+ },
41
+ "4": {
42
+ "level": 4,
43
+ "stimulation_size": 360,
44
+ "reward_delay": 300
45
+ },
46
+ "5": {
47
+ "level": 5,
48
+ "stimulation_size": 345,
49
+ "reward_delay": 400
50
+ },
51
+ "6": {
52
+ "level": 6,
53
+ "stimulation_size": 330,
54
+ "reward_delay": 500
55
+ },
56
+ "7": {
57
+ "level": 7,
58
+ "stimulation_size": 315,
59
+ "reward_delay": 600
60
+ },
61
+ "8": {
62
+ "level": 8,
63
+ "stimulation_size": 300,
64
+ "reward_delay": 700
65
+ },
66
+ "9": {
67
+ "level": 9,
68
+ "stimulation_size": 285,
69
+ "reward_delay": 800
70
+ },
71
+ "10": {
72
+ "level": 10,
73
+ "stimulation_size": 270,
74
+ "reward_delay": 900
75
+ },
76
+ "11": {
77
+ "level": 11,
78
+ "stimulation_size": 260,
79
+ "reward_delay": 1000
80
+ }
81
+ }
82
+ }
83
+ }
@@ -0,0 +1,58 @@
1
+ from pathlib import Path
2
+
3
+ from mxbi.config import Configure
4
+ from mxbi.models.animal import ScheduleCondition
5
+ from mxbi.tasks.GNGSiD.models import LevelID, MonkeyName
6
+ from pydantic import BaseModel, ConfigDict, RootModel
7
+
8
+ CONFIG_PATH = Path(__file__).parent / "config.json"
9
+
10
+
11
+ class SizeReductionStageParameters(BaseModel):
12
+ model_config = ConfigDict(frozen=True)
13
+
14
+ # audio stimulation config
15
+ stimulus_duration: int
16
+ stimulus_freq: int
17
+ stimulus_freq_duration: int
18
+ stimulus_interval: int
19
+
20
+ # trial lifecycle
21
+ time_out: int
22
+ inter_trial_interval: int
23
+
24
+ # reward config
25
+ reward_duration: int
26
+
27
+
28
+ class SizeReductionStageLeveledParameters(BaseModel):
29
+ model_config = ConfigDict(frozen=True)
30
+
31
+ level: int
32
+ stimulation_size: int
33
+ reward_delay: int
34
+
35
+
36
+ class SizeReductionStageConfig(BaseModel):
37
+ model_config = ConfigDict(frozen=True)
38
+
39
+ condition: ScheduleCondition
40
+
41
+ params: SizeReductionStageParameters
42
+ levels_table: dict[LevelID, SizeReductionStageLeveledParameters]
43
+
44
+
45
+ class SizeReductionStageConfigs(RootModel):
46
+ model_config = ConfigDict(frozen=True)
47
+
48
+ root: dict[MonkeyName, SizeReductionStageConfig]
49
+
50
+
51
+ def load_config() -> SizeReductionStageConfigs:
52
+ configs = Configure(CONFIG_PATH, SizeReductionStageConfigs).value
53
+ for config in configs.root.values():
54
+ config.condition.level_count = len(config.levels_table)
55
+ return configs
56
+
57
+
58
+ config = load_config()