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.
- mxbiflow/__init__.py +3 -0
- mxbiflow/assets/__init__.py +5 -0
- mxbiflow/assets/clicker.wav +0 -0
- mxbiflow/config_store.py +68 -0
- mxbiflow/data_logger.py +114 -0
- mxbiflow/default/__init__.py +4 -0
- mxbiflow/default/idle/assets/apple_v1.png +0 -0
- mxbiflow/default/idle/idle.py +57 -0
- mxbiflow/detector_bridge.py +87 -0
- mxbiflow/game.py +84 -0
- mxbiflow/infra/eventbus.py +31 -0
- mxbiflow/main.py +106 -0
- mxbiflow/models/animal.py +130 -0
- mxbiflow/models/reward.py +7 -0
- mxbiflow/models/session.py +145 -0
- mxbiflow/mxbiflow.py +43 -0
- mxbiflow/path.py +41 -0
- mxbiflow/scene/__init__.py +8 -0
- mxbiflow/scene/scene_manager.py +64 -0
- mxbiflow/scene/scene_protocol.py +22 -0
- mxbiflow/scheduler.py +90 -0
- mxbiflow/tasks/GNGSiD/models.py +70 -0
- mxbiflow/tasks/GNGSiD/stages/detect_stage/config.json +116 -0
- mxbiflow/tasks/GNGSiD/stages/detect_stage/detect_stage.py +161 -0
- mxbiflow/tasks/GNGSiD/stages/detect_stage/detect_stage_models.py +65 -0
- mxbiflow/tasks/GNGSiD/stages/discriminate_stage/config.json +70 -0
- mxbiflow/tasks/GNGSiD/stages/discriminate_stage/discriminate_stage.py +173 -0
- mxbiflow/tasks/GNGSiD/stages/discriminate_stage/discriminate_stage_models.py +80 -0
- mxbiflow/tasks/GNGSiD/stages/size_reduction_stage/config.json +83 -0
- mxbiflow/tasks/GNGSiD/stages/size_reduction_stage/size_reduction_models.py +58 -0
- mxbiflow/tasks/GNGSiD/stages/size_reduction_stage/size_reduction_stage.py +149 -0
- mxbiflow/tasks/GNGSiD/tasks/artifacts.py +13 -0
- mxbiflow/tasks/GNGSiD/tasks/detect/models.py +21 -0
- mxbiflow/tasks/GNGSiD/tasks/detect/scene.py +271 -0
- mxbiflow/tasks/GNGSiD/tasks/discriminate/discriminate_models.py +31 -0
- mxbiflow/tasks/GNGSiD/tasks/discriminate/discriminate_scene.py +336 -0
- mxbiflow/tasks/GNGSiD/tasks/touch/touch_models.py +17 -0
- mxbiflow/tasks/GNGSiD/tasks/touch/touch_scene.py +256 -0
- mxbiflow/tasks/GNGSiD/tasks/utils/targets.py +57 -0
- mxbiflow/tasks/cross_modal/bundle_dir.py +553 -0
- mxbiflow/tasks/cross_modal/config.py +41 -0
- mxbiflow/tasks/cross_modal/media.py +61 -0
- mxbiflow/tasks/cross_modal/models.py +57 -0
- mxbiflow/tasks/cross_modal/scene.py +252 -0
- mxbiflow/tasks/cross_modal/stage.py +218 -0
- mxbiflow/tasks/cross_modal/trial_io.py +23 -0
- mxbiflow/tasks/cross_modal/trial_schema.py +113 -0
- mxbiflow/tasks/default/error_task/error_scene.py +53 -0
- mxbiflow/tasks/default/idle_task/assets/apple_v1.png +0 -0
- mxbiflow/tasks/default/idle_task/idle_scene.py +85 -0
- mxbiflow/tasks/default/initial_habituation_training/README.md +188 -0
- mxbiflow/tasks/default/initial_habituation_training/stages/config.csv +7 -0
- mxbiflow/tasks/default/initial_habituation_training/stages/config.json +67 -0
- mxbiflow/tasks/default/initial_habituation_training/stages/initial_habituation_training_stage.py +172 -0
- mxbiflow/tasks/default/initial_habituation_training/stages/models.py +56 -0
- mxbiflow/tasks/default/initial_habituation_training/tasks/stay_to_reward/stay_to_reward.py +244 -0
- mxbiflow/tasks/default/initial_habituation_training/tasks/stay_to_reward/stay_to_reward_models.py +50 -0
- mxbiflow/tasks/task_protocol.py +26 -0
- mxbiflow/tasks/task_table.py +29 -0
- mxbiflow/tasks/two_alternative_choice/assets/starter.py +27 -0
- mxbiflow/tasks/two_alternative_choice/models.py +68 -0
- mxbiflow/tasks/two_alternative_choice/stages/size_reduction_stage/config.json +118 -0
- mxbiflow/tasks/two_alternative_choice/stages/size_reduction_stage/size_reduction_models.py +41 -0
- mxbiflow/tasks/two_alternative_choice/stages/size_reduction_stage/size_reduction_stage.py +122 -0
- mxbiflow/tasks/two_alternative_choice/tasks/touch/touch_models.py +19 -0
- mxbiflow/tasks/two_alternative_choice/tasks/touch/touch_scene.py +249 -0
- mxbiflow/timer/__init__.py +3 -0
- mxbiflow/timer/frame_timer.py +47 -0
- mxbiflow/timer/realtime_timer.py +0 -0
- mxbiflow/tmp_email.py +13 -0
- mxbiflow/ui/components/animal.py +87 -0
- mxbiflow/ui/components/baseconfig.py +68 -0
- mxbiflow/ui/components/card.py +18 -0
- mxbiflow/ui/components/device_card/__init__.py +17 -0
- mxbiflow/ui/components/device_card/detector/beambreak_detector_card.py +29 -0
- mxbiflow/ui/components/device_card/detector/fusion_detector.py +45 -0
- mxbiflow/ui/components/device_card/detector/mock_detector_card.py +20 -0
- mxbiflow/ui/components/device_card/detector/rfid_detector.py +40 -0
- mxbiflow/ui/components/device_card/device_card.py +67 -0
- mxbiflow/ui/components/device_card/rewarder/mock_rewarder_card.py +20 -0
- mxbiflow/ui/components/device_card/rewarder/rpi_gpio_rewarder.py +33 -0
- mxbiflow/ui/components/devices.py +183 -0
- mxbiflow/ui/components/dialog/__init__.py +3 -0
- mxbiflow/ui/components/dialog/add_devices_dialog.py +64 -0
- mxbiflow/ui/components/experiment_groups.py +122 -0
- mxbiflow/ui/experiment_panel.py +91 -0
- mxbiflow/ui/mxbi_panel.py +152 -0
- mxbiflow/utils/logger.py +19 -0
- mxbiflow/utils/serial.py +10 -0
- mxbiflow-0.1.1.dist-info/METADATA +168 -0
- mxbiflow-0.1.1.dist-info/RECORD +93 -0
- mxbiflow-0.1.1.dist-info/WHEEL +4 -0
- mxbiflow-0.1.1.dist-info/entry_points.txt +4 -0
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from math import ceil
|
|
3
|
+
from random import uniform
|
|
4
|
+
from tkinter import Frame
|
|
5
|
+
from typing import TYPE_CHECKING, Final
|
|
6
|
+
|
|
7
|
+
from mxbi.tasks.default.initial_habituation_training.tasks.stay_to_reward.stay_to_reward_models import (
|
|
8
|
+
DataToShow,
|
|
9
|
+
Result,
|
|
10
|
+
TrialData,
|
|
11
|
+
)
|
|
12
|
+
from mxbi.utils.aplayer import ToneConfig
|
|
13
|
+
from mxbi.utils.tkinter.components.canvas_with_border import CanvasWithInnerBorder
|
|
14
|
+
from mxbi.utils.tkinter.components.showdata_widget import ShowDataWidget
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from concurrent.futures import Future
|
|
18
|
+
|
|
19
|
+
from mxbi.models.animal import AnimalState
|
|
20
|
+
from mxbi.models.session import ScreenConfig
|
|
21
|
+
from mxbi.tasks.default.initial_habituation_training.stages.models import (
|
|
22
|
+
StageContext,
|
|
23
|
+
)
|
|
24
|
+
from mxbi.tasks.default.initial_habituation_training.tasks.stay_to_reward.stay_to_reward_models import (
|
|
25
|
+
TrialConfig,
|
|
26
|
+
)
|
|
27
|
+
from mxbi.theater import Theater
|
|
28
|
+
from numpy import int16
|
|
29
|
+
from numpy.typing import NDArray
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class DefaultStayToRewardScene:
|
|
33
|
+
def __init__(
|
|
34
|
+
self,
|
|
35
|
+
theater: "Theater",
|
|
36
|
+
animal_state: "AnimalState",
|
|
37
|
+
screen_type: "ScreenConfig",
|
|
38
|
+
trial_config: "TrialConfig",
|
|
39
|
+
context: "StageContext",
|
|
40
|
+
background: "CanvasWithInnerBorder",
|
|
41
|
+
):
|
|
42
|
+
self._theater: "Final[Theater]" = theater
|
|
43
|
+
self._animal_state: "Final[AnimalState]" = animal_state
|
|
44
|
+
self._screen_type: "Final[ScreenConfig]" = screen_type
|
|
45
|
+
self._trial_config: "Final[TrialConfig]" = trial_config
|
|
46
|
+
self._context: "Final[StageContext]" = context
|
|
47
|
+
self._background = background
|
|
48
|
+
|
|
49
|
+
self._play_future: Future[bool] | None = None
|
|
50
|
+
|
|
51
|
+
self._tone = self._prepare_stimulus()
|
|
52
|
+
self._standard_reward_stimulus = self._theater.new_standard_reward_stimulus(
|
|
53
|
+
self._trial_config.stimulus_duration
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
self._set_stimulus_intensity()
|
|
57
|
+
self._on_trial_start()
|
|
58
|
+
|
|
59
|
+
# region public api
|
|
60
|
+
def start(self) -> "TrialData":
|
|
61
|
+
self._theater.mainloop()
|
|
62
|
+
|
|
63
|
+
return self._data
|
|
64
|
+
|
|
65
|
+
def cancle(self) -> None:
|
|
66
|
+
self._cleanup()
|
|
67
|
+
|
|
68
|
+
# endregion
|
|
69
|
+
|
|
70
|
+
# region lifecycle
|
|
71
|
+
def _on_trial_start(self) -> None:
|
|
72
|
+
self._create_view()
|
|
73
|
+
self._init_data()
|
|
74
|
+
self._bind_events()
|
|
75
|
+
self._start_tracking_data()
|
|
76
|
+
|
|
77
|
+
if (
|
|
78
|
+
self._trial_config.entry_reward
|
|
79
|
+
and self._animal_state.current_animal_session_trial_id == 1
|
|
80
|
+
):
|
|
81
|
+
self._direct_stimulus()
|
|
82
|
+
else:
|
|
83
|
+
self._start_timing()
|
|
84
|
+
self._stimulus_loop()
|
|
85
|
+
|
|
86
|
+
def _on_trial_end(self) -> None:
|
|
87
|
+
if self._play_future is not None:
|
|
88
|
+
self._play_future.add_done_callback(lambda _: self._cleanup())
|
|
89
|
+
else:
|
|
90
|
+
self._cleanup()
|
|
91
|
+
|
|
92
|
+
def _cleanup(self) -> None:
|
|
93
|
+
self._data.trial_end_time = datetime.now().timestamp()
|
|
94
|
+
self._data.stay_duration = (
|
|
95
|
+
self._data.trial_end_time - self._data.trial_start_time
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
self._theater.aplayer.stop()
|
|
99
|
+
self._trigger.destroy()
|
|
100
|
+
self._show_data_widget.destroy()
|
|
101
|
+
self._theater.root.quit()
|
|
102
|
+
|
|
103
|
+
# endregion
|
|
104
|
+
|
|
105
|
+
# region views
|
|
106
|
+
|
|
107
|
+
def _create_view(self) -> None:
|
|
108
|
+
self._create_background()
|
|
109
|
+
self._create_trigger()
|
|
110
|
+
self._create_show_data_widget()
|
|
111
|
+
|
|
112
|
+
def _create_background(self) -> None:
|
|
113
|
+
self._background.place(relx=0.5, rely=0.5, anchor="center")
|
|
114
|
+
|
|
115
|
+
def _create_trigger(self) -> None:
|
|
116
|
+
self._trigger = Frame(bg="red", width=0, height=0)
|
|
117
|
+
self._trigger.place(relx=0.5, rely=0.5, anchor="center")
|
|
118
|
+
self._trigger.lower()
|
|
119
|
+
|
|
120
|
+
def _create_show_data_widget(self) -> None:
|
|
121
|
+
self._show_data_widget = ShowDataWidget(self._background)
|
|
122
|
+
self._show_data_widget.place(relx=0, rely=1, anchor="sw")
|
|
123
|
+
_data = DataToShow(
|
|
124
|
+
name=self._animal_state.name,
|
|
125
|
+
ald=self._animal_state.current_animal_session_trial_id,
|
|
126
|
+
level=self._animal_state.level,
|
|
127
|
+
id=self._animal_state.trial_id,
|
|
128
|
+
lid=self._animal_state.current_level_trial_id,
|
|
129
|
+
t_dur=f"{self._context.duration} s",
|
|
130
|
+
dur="0 s",
|
|
131
|
+
rewards=0,
|
|
132
|
+
)
|
|
133
|
+
self._show_data_widget.show_data(_data.model_dump())
|
|
134
|
+
|
|
135
|
+
# endregion
|
|
136
|
+
|
|
137
|
+
# region event binding
|
|
138
|
+
def _bind_events(self) -> None:
|
|
139
|
+
self._trigger.focus_set()
|
|
140
|
+
|
|
141
|
+
def _start_timing(self) -> None:
|
|
142
|
+
target_ms = int(self._trial_config.target * 1000)
|
|
143
|
+
self._trigger.after(target_ms, self._on_correct)
|
|
144
|
+
|
|
145
|
+
def _stimulus_loop(self) -> None:
|
|
146
|
+
stimulus_interval = uniform(
|
|
147
|
+
self._trial_config.min_stimulus_interval,
|
|
148
|
+
self._trial_config.max_stimulus_interval,
|
|
149
|
+
)
|
|
150
|
+
stimulus_interval_ms = int(stimulus_interval * 1000)
|
|
151
|
+
|
|
152
|
+
self._trigger.after(stimulus_interval_ms, self._give_stimulus)
|
|
153
|
+
|
|
154
|
+
# endregion
|
|
155
|
+
|
|
156
|
+
# region data
|
|
157
|
+
def _start_tracking_data(self) -> None:
|
|
158
|
+
data = DataToShow(
|
|
159
|
+
name=self._animal_state.name,
|
|
160
|
+
ald=self._animal_state.current_animal_session_trial_id,
|
|
161
|
+
level=self._animal_state.level,
|
|
162
|
+
id=self._animal_state.trial_id,
|
|
163
|
+
lid=self._animal_state.current_level_trial_id,
|
|
164
|
+
t_dur=f"{self._context.duration} s",
|
|
165
|
+
dur=f"{int(self._data.stay_duration)} s",
|
|
166
|
+
rewards=self._context.rewards,
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
self._data.stay_duration += 1
|
|
170
|
+
self._context.duration += 1
|
|
171
|
+
|
|
172
|
+
self._show_data_widget.update_data(data.model_dump())
|
|
173
|
+
self._trigger.after(1000, self._start_tracking_data)
|
|
174
|
+
|
|
175
|
+
def _init_data(self) -> None:
|
|
176
|
+
self._data = TrialData(
|
|
177
|
+
level=self._trial_config.level,
|
|
178
|
+
level_trial_id=self._animal_state.current_level_trial_id,
|
|
179
|
+
animal_session_trial_id=self._animal_state.current_animal_session_trial_id,
|
|
180
|
+
animal=self._animal_state.name,
|
|
181
|
+
trial_id=self._animal_state.trial_id,
|
|
182
|
+
trial_start_time=datetime.now().timestamp(),
|
|
183
|
+
trial_end_time=0,
|
|
184
|
+
stay_duration=0,
|
|
185
|
+
result=Result.CORRECT,
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
# endregion
|
|
189
|
+
|
|
190
|
+
# region stimulus and reward
|
|
191
|
+
def _prepare_stimulus(self) -> "NDArray[int16]":
|
|
192
|
+
unit_duration = (
|
|
193
|
+
self._trial_config.stimulus_freq_duration
|
|
194
|
+
+ self._trial_config.stimulus_freq_interval
|
|
195
|
+
)
|
|
196
|
+
times = ceil(self._trial_config.stimulus_duration / unit_duration)
|
|
197
|
+
times = max(times, 1)
|
|
198
|
+
|
|
199
|
+
freq_1 = ToneConfig(
|
|
200
|
+
frequency=self._trial_config.stimulus_freq,
|
|
201
|
+
duration=self._trial_config.stimulus_freq_duration,
|
|
202
|
+
)
|
|
203
|
+
freq_2 = ToneConfig(
|
|
204
|
+
frequency=0,
|
|
205
|
+
duration=self._trial_config.stimulus_freq_interval,
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
return self._theater.aplayer.generate_stimulus([freq_1, freq_2], times)
|
|
209
|
+
|
|
210
|
+
def _set_stimulus_intensity(self) -> None:
|
|
211
|
+
self._theater.acontroller.set_master_volume(self._trial_config.stimulus_density)
|
|
212
|
+
|
|
213
|
+
def _give_stimulus(self) -> None:
|
|
214
|
+
self._play_future = self._theater.aplayer.play_stimulus(self._tone)
|
|
215
|
+
self._play_future.add_done_callback(self._on_stimulus_complete)
|
|
216
|
+
|
|
217
|
+
def _direct_stimulus(self) -> None:
|
|
218
|
+
self._play_future = self._theater.aplayer.play_stimulus(self._tone)
|
|
219
|
+
self._play_future.add_done_callback(self._on_direct_stimulus_complete)
|
|
220
|
+
|
|
221
|
+
def _on_direct_stimulus_complete(self, future: "Future[bool]") -> None:
|
|
222
|
+
if future.result():
|
|
223
|
+
self._trigger.after(0, self._give_reward)
|
|
224
|
+
|
|
225
|
+
self._trigger.after(
|
|
226
|
+
self._trial_config.reward_duration,
|
|
227
|
+
lambda: (self._start_timing(), self._stimulus_loop()),
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
def _on_stimulus_complete(self, future: "Future[bool]") -> None:
|
|
231
|
+
if future.result():
|
|
232
|
+
self._trigger.after(0, self._give_reward)
|
|
233
|
+
|
|
234
|
+
self._trigger.after(self._trial_config.reward_duration, self._stimulus_loop)
|
|
235
|
+
|
|
236
|
+
def _on_correct(self) -> None:
|
|
237
|
+
self._data.result = Result.CORRECT
|
|
238
|
+
self._on_trial_end()
|
|
239
|
+
|
|
240
|
+
def _give_reward(self) -> None:
|
|
241
|
+
self._context.rewards += 1
|
|
242
|
+
self._theater.reward.give_reward(self._trial_config.reward_duration)
|
|
243
|
+
|
|
244
|
+
# endregion
|
mxbiflow/tasks/default/initial_habituation_training/tasks/stay_to_reward/stay_to_reward_models.py
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from enum import StrEnum, auto
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Result(StrEnum):
|
|
7
|
+
CORRECT = auto()
|
|
8
|
+
INCORRECT = auto()
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class TrialConfig(BaseModel):
|
|
12
|
+
level: int
|
|
13
|
+
entry_reward: bool
|
|
14
|
+
|
|
15
|
+
min_stimulus_interval: float # seconds
|
|
16
|
+
max_stimulus_interval: float # seconds
|
|
17
|
+
target: float # seconds
|
|
18
|
+
|
|
19
|
+
reward_duration: int = 1000 # milliseconds
|
|
20
|
+
stimulus_duration: int = 1000 # milliseconds
|
|
21
|
+
|
|
22
|
+
stimulus_density: int # volume 0-100
|
|
23
|
+
|
|
24
|
+
stimulus_freq: int = 2000
|
|
25
|
+
stimulus_freq_duration: int = 100
|
|
26
|
+
stimulus_freq_interval: int = 100
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class TrialData(BaseModel):
|
|
30
|
+
level: int
|
|
31
|
+
animal: str
|
|
32
|
+
trial_id: int
|
|
33
|
+
level_trial_id: int
|
|
34
|
+
animal_session_trial_id: int
|
|
35
|
+
trial_start_time: float
|
|
36
|
+
trial_end_time: float
|
|
37
|
+
result: Result
|
|
38
|
+
|
|
39
|
+
stay_duration: float
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class DataToShow(BaseModel):
|
|
43
|
+
level: int
|
|
44
|
+
name: str
|
|
45
|
+
id: int
|
|
46
|
+
lid: int
|
|
47
|
+
ald: int
|
|
48
|
+
t_dur: str
|
|
49
|
+
dur: str
|
|
50
|
+
rewards: int
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, Protocol
|
|
2
|
+
|
|
3
|
+
if TYPE_CHECKING:
|
|
4
|
+
from mxbi.models.animal import AnimalState, ScheduleCondition
|
|
5
|
+
from mxbi.models.session import SessionState
|
|
6
|
+
from mxbi.models.task import Feedback
|
|
7
|
+
from mxbi.theater import Theater
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Task(Protocol):
|
|
11
|
+
def __init__(
|
|
12
|
+
self,
|
|
13
|
+
theater: "Theater",
|
|
14
|
+
session_state: "SessionState",
|
|
15
|
+
animal_state: "AnimalState",
|
|
16
|
+
) -> None: ...
|
|
17
|
+
def start(self) -> "Feedback": ...
|
|
18
|
+
|
|
19
|
+
def quit(self) -> None: ...
|
|
20
|
+
|
|
21
|
+
def on_idle(self) -> None: ...
|
|
22
|
+
|
|
23
|
+
def on_return(self) -> None: ...
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
def condition(self) -> "ScheduleCondition | None": ...
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from mxbi.models.task import TaskEnum
|
|
2
|
+
from mxbi.tasks.cross_modal.stage import CrossModalTask
|
|
3
|
+
from mxbi.tasks.default.error_task.error_scene import ErrorScene
|
|
4
|
+
from mxbi.tasks.default.idle_task.idle_scene import IDLEScene
|
|
5
|
+
from mxbi.tasks.default.initial_habituation_training.stages.initial_habituation_training_stage import (
|
|
6
|
+
InitialHabituationTrainingStage,
|
|
7
|
+
)
|
|
8
|
+
from mxbi.tasks.GNGSiD.stages.detect_stage.detect_stage import GNGSiDDetectStage
|
|
9
|
+
from mxbi.tasks.GNGSiD.stages.discriminate_stage.discriminate_stage import (
|
|
10
|
+
GNGSiDDiscriminateStage,
|
|
11
|
+
)
|
|
12
|
+
from mxbi.tasks.GNGSiD.stages.size_reduction_stage.size_reduction_stage import (
|
|
13
|
+
SizeReductionStage,
|
|
14
|
+
)
|
|
15
|
+
from mxbi.tasks.task_protocol import Task
|
|
16
|
+
from mxbi.tasks.two_alternative_choice.stages.size_reduction_stage.size_reduction_stage import (
|
|
17
|
+
TWOACSizeReductionStage,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
task_table: dict[TaskEnum, type[Task]] = {
|
|
21
|
+
TaskEnum.IDEL: IDLEScene,
|
|
22
|
+
TaskEnum.ERROR: ErrorScene,
|
|
23
|
+
TaskEnum.HABITUATION: InitialHabituationTrainingStage,
|
|
24
|
+
TaskEnum.GNGSiD_SIZE_REDUCTION_STAGE: SizeReductionStage,
|
|
25
|
+
TaskEnum.GNGSiD_DETECT_STAGE: GNGSiDDetectStage,
|
|
26
|
+
TaskEnum.GNGSiD_DISCRIMINATE_STAGE: GNGSiDDiscriminateStage,
|
|
27
|
+
TaskEnum.TWOAC_SIZE_REDUCTION_STAGE: TWOACSizeReductionStage,
|
|
28
|
+
TaskEnum.CROSS_MODAL: CrossModalTask,
|
|
29
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from tkinter import Canvas
|
|
2
|
+
|
|
3
|
+
from mxbi.utils.tkinter.create_circle import create_circle
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Starter(Canvas):
|
|
7
|
+
def __init__(self, master, size) -> None:
|
|
8
|
+
super().__init__(
|
|
9
|
+
master, width=size, height=size, bg="blue", highlightthickness=0
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
create_circle(
|
|
13
|
+
size * 0.5,
|
|
14
|
+
size * 0.5,
|
|
15
|
+
(size - 1) / 2,
|
|
16
|
+
self,
|
|
17
|
+
"white",
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
if __name__ == "__main__":
|
|
22
|
+
from tkinter import Tk
|
|
23
|
+
|
|
24
|
+
root = Tk()
|
|
25
|
+
root.geometry("300x300")
|
|
26
|
+
Starter(root, 300).pack()
|
|
27
|
+
root.mainloop()
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
from enum import StrEnum, auto
|
|
2
|
+
from typing import TypeAlias
|
|
3
|
+
|
|
4
|
+
from pydantic import BaseModel
|
|
5
|
+
|
|
6
|
+
LevelID: TypeAlias = int
|
|
7
|
+
MonkeyName: TypeAlias = str
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Result(StrEnum):
|
|
11
|
+
CORRECT = auto()
|
|
12
|
+
INCORRECT = auto()
|
|
13
|
+
TIMEOUT = auto()
|
|
14
|
+
CANCEL = auto()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class TouchEvent(BaseModel):
|
|
18
|
+
time: float
|
|
19
|
+
x: int
|
|
20
|
+
y: int
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class BaseTrialConfig(BaseModel):
|
|
24
|
+
# basic config
|
|
25
|
+
level: int = 0
|
|
26
|
+
|
|
27
|
+
# visual stimulation config
|
|
28
|
+
stimulation_size: int = 0
|
|
29
|
+
|
|
30
|
+
# audio stimulation config
|
|
31
|
+
stimulus_duration: int = 0
|
|
32
|
+
|
|
33
|
+
# trial lifecycle
|
|
34
|
+
time_out: int = 0
|
|
35
|
+
inter_trial_interval: int = 0
|
|
36
|
+
|
|
37
|
+
# reward config
|
|
38
|
+
reward_duration: int = 0
|
|
39
|
+
reward_delay: int = 0
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class BaseTrialData(BaseModel):
|
|
43
|
+
animal: str
|
|
44
|
+
trial_id: int
|
|
45
|
+
current_level_trial_id: int
|
|
46
|
+
trial_start_time: float
|
|
47
|
+
trial_end_time: float
|
|
48
|
+
result: Result
|
|
49
|
+
correct_rate: float
|
|
50
|
+
touch_events: list[TouchEvent]
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class BaseDataToShow(BaseModel):
|
|
54
|
+
name: str
|
|
55
|
+
id: int
|
|
56
|
+
level_id: int
|
|
57
|
+
level: int
|
|
58
|
+
rewards: int
|
|
59
|
+
correct: int
|
|
60
|
+
incorrect: int
|
|
61
|
+
timeout: int
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class PersistentData(BaseModel):
|
|
65
|
+
rewards: int
|
|
66
|
+
correct: int
|
|
67
|
+
incorrect: int
|
|
68
|
+
timeout: int
|
|
@@ -0,0 +1,118 @@
|
|
|
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
|
+
"trial_config": {
|
|
12
|
+
"stimulus_duration": 1000,
|
|
13
|
+
"time_out": 10000,
|
|
14
|
+
"inter_trial_interval": 1000,
|
|
15
|
+
"reward_duration": 1000,
|
|
16
|
+
"reward_delay": 0,
|
|
17
|
+
"stimulus_freq": 2000,
|
|
18
|
+
"stimulus_freq_duration": 100,
|
|
19
|
+
"stimulus_interval": 100
|
|
20
|
+
},
|
|
21
|
+
"levels_table": {
|
|
22
|
+
"1": {
|
|
23
|
+
"level": 1,
|
|
24
|
+
"stimulation_size": 300
|
|
25
|
+
},
|
|
26
|
+
"2": {
|
|
27
|
+
"level": 2,
|
|
28
|
+
"stimulation_size": 295
|
|
29
|
+
},
|
|
30
|
+
"3": {
|
|
31
|
+
"level": 3,
|
|
32
|
+
"stimulation_size": 390
|
|
33
|
+
},
|
|
34
|
+
"4": {
|
|
35
|
+
"level": 4,
|
|
36
|
+
"stimulation_size": 285
|
|
37
|
+
},
|
|
38
|
+
"5": {
|
|
39
|
+
"level": 5,
|
|
40
|
+
"stimulation_size": 280
|
|
41
|
+
},
|
|
42
|
+
"6": {
|
|
43
|
+
"level": 6,
|
|
44
|
+
"stimulation_size": 275
|
|
45
|
+
},
|
|
46
|
+
"7": {
|
|
47
|
+
"level": 7,
|
|
48
|
+
"stimulation_size": 270
|
|
49
|
+
},
|
|
50
|
+
"8": {
|
|
51
|
+
"level": 8,
|
|
52
|
+
"stimulation_size": 260
|
|
53
|
+
},
|
|
54
|
+
"9": {
|
|
55
|
+
"level": 9,
|
|
56
|
+
"stimulation_size": 255
|
|
57
|
+
},
|
|
58
|
+
"10": {
|
|
59
|
+
"level": 10,
|
|
60
|
+
"stimulation_size": 240
|
|
61
|
+
},
|
|
62
|
+
"11": {
|
|
63
|
+
"level": 11,
|
|
64
|
+
"stimulation_size": 230
|
|
65
|
+
},
|
|
66
|
+
"12": {
|
|
67
|
+
"level": 12,
|
|
68
|
+
"stimulation_size": 225
|
|
69
|
+
},
|
|
70
|
+
"13": {
|
|
71
|
+
"level": 13,
|
|
72
|
+
"stimulation_size": 220
|
|
73
|
+
},
|
|
74
|
+
"14": {
|
|
75
|
+
"level": 14,
|
|
76
|
+
"stimulation_size": 210
|
|
77
|
+
},
|
|
78
|
+
"15": {
|
|
79
|
+
"level": 15,
|
|
80
|
+
"stimulation_size": 200
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
"debug": {
|
|
85
|
+
"condition": {
|
|
86
|
+
"config": {
|
|
87
|
+
"evaluation_interval": 2,
|
|
88
|
+
"difficulty_increase_threshold": 0.8,
|
|
89
|
+
"difficulty_decrease_threshold": 0.45,
|
|
90
|
+
"next_task": "gngsid_detect_stage"
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
"trial_config": {
|
|
94
|
+
"stimulus_duration": 1000,
|
|
95
|
+
"time_out": 10000,
|
|
96
|
+
"inter_trial_interval": 1000,
|
|
97
|
+
"reward_duration": 1000,
|
|
98
|
+
"reward_delay": 0,
|
|
99
|
+
"stimulus_freq": 2000,
|
|
100
|
+
"stimulus_freq_duration": 100,
|
|
101
|
+
"stimulus_interval": 100
|
|
102
|
+
},
|
|
103
|
+
"levels_table": {
|
|
104
|
+
"1": {
|
|
105
|
+
"level": 1,
|
|
106
|
+
"stimulation_size": 300
|
|
107
|
+
},
|
|
108
|
+
"2": {
|
|
109
|
+
"level": 2,
|
|
110
|
+
"stimulation_size": 295
|
|
111
|
+
},
|
|
112
|
+
"3": {
|
|
113
|
+
"level": 3,
|
|
114
|
+
"stimulation_size": 390
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from mxbi.config import Configure
|
|
4
|
+
from mxbi.models.animal import ScheduleCondition
|
|
5
|
+
from mxbi.tasks.two_alternative_choice.models import LevelID, MonkeyName
|
|
6
|
+
from mxbi.tasks.two_alternative_choice.tasks.touch.touch_models import TrialConfig
|
|
7
|
+
from pydantic import BaseModel, ConfigDict, RootModel
|
|
8
|
+
|
|
9
|
+
CONFIG_PATH = Path(__file__).parent / "config.json"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class SizeReductionStageLeveledParameters(BaseModel):
|
|
13
|
+
model_config = ConfigDict(frozen=True)
|
|
14
|
+
|
|
15
|
+
level: int
|
|
16
|
+
stimulation_size: int
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class SizeReductionStageConfig(BaseModel):
|
|
20
|
+
model_config = ConfigDict(frozen=True)
|
|
21
|
+
|
|
22
|
+
condition: ScheduleCondition
|
|
23
|
+
trial_config: TrialConfig
|
|
24
|
+
|
|
25
|
+
levels_table: dict[LevelID, SizeReductionStageLeveledParameters]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class SizeReductionStageConfigs(RootModel):
|
|
29
|
+
model_config = ConfigDict(frozen=True)
|
|
30
|
+
|
|
31
|
+
root: dict[MonkeyName, SizeReductionStageConfig]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def load_config() -> SizeReductionStageConfigs:
|
|
35
|
+
configs = Configure(CONFIG_PATH, SizeReductionStageConfigs).value
|
|
36
|
+
for config in configs.root.values():
|
|
37
|
+
config.condition.level_count = len(config.levels_table)
|
|
38
|
+
return configs
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
config = load_config()
|