Hi Chris,
Sorry for the late reply. I was able to test the code but encountered some issues and have a few questions.
Issues:
I wanted to implement the code in a 2AFC task. Our programs/scripts are organized such that we have a folder for appendables (configuration of hardware, sounds, special stimuli) and a folder for tasks (all the tasks that a monkey can perform). Typically, we have these folders plus a master script/protocol per monkey that appends its tasks, hardware to use, and specific preferences of that monkey.
I added the QUEST scripts to our appendables folder. When I append them, I get an error stating that quest_staircase.py or quest.py cannot be found.
I solved this by making a copy of the .py files in the folder where the master script lives.
However, at the beginning of each experiment, I load multiple sound files,
e.g. sound/wav_file reward_sound (path = ‘/Users/lab/Documents/Experiment_Sounds/reward.wav’).
This works fine for all our experiment. But then I add the QUEST staircase, and I get the following error:
ERROR: Failed to create object.
Extended information:
reason: Path does not exist: /var/folders/jh/2_67mn016sndl5nfdn2h28sc0000gr/T/MWorks/Experiment Cache/\_Users_lab_Documents_GitHub_SRG_repo_Projects_Optogenetics_Experiment_Test_staircase2AFCT.mwel/tmp/Users/lab/Documents/Experiment_Sounds/reward.wav
location: IO_devices_Variables_human_psychophysics.mwel: line 203, column 1
object_type: sound/audio_file
ref_id: idp105553148889343
component: reward_sound
parser_context: mw_create
Therefore, when I add the QUEST staircase, we are unable to play any sound.
Regarding using QUEST in MWorks:
From what I understood, to initialize the staircase, I need to set the variables that control the Weibull distribution:
quest_t_guess = .75
quest_t_guess_sd = .2
quest_p_threshold = 0.8
quest_beta = 3.5
quest_delta = 0.1
quest_gamma = 0.5
Then set an ID:
quest_state_id = 0
And then use:
quest_reset()
If I change the staircase for another condition, I need to change the variables that control the Weibull distribution, choose another ID, and then use quest_reset() again.
Is this correct?
If so, I have a couple of questions.
Let’s assume I am in state_id 0 and I do not change state. Then I change the mean of the Weibull distribution and use quest_reset(). Does this reset all values from QUEST, updating the mean of the staircase?
If so, is the QUEST object storing the previously used intensities and responses, or does pressing reset erase everything and revert to the first assigned values?
I ask because it would be useful to switch between different staircases for different conditions (which I assume is what the state_id setting is for). But it is not clear how to assign starting values, purge or overwrite them, and whether doing so erases everything or keeps the previously collected responses and intensities (but just doesn’t use them).
I mention this because, in one test, QUEST suggested values outside the physically possible range. Although my program corrects values that exceed limits, QUEST continued diverging. That’s why I think it would be useful to have a command to purge the staircase for a given ID.
Next, would it be possible to define minimum and maximum values to prevent out-of-bounds issues?
About the next stimulus: so far, I have been using quest_mean() to select the next intensity. From what I understand, if I set quest_p_threshold = 0.82, QUEST centers the mean of the distribution around the intensity value that corresponds to this probability of correct detection. This is useful, but I also saw a method called calculateNextIntensity:
def calculateNextIntensity(self):
"""based on current intensity and counter of correct responses"""
self._intensity()
# Check we haven’t gone out of the legal range
if self.maxVal is not None and self._nextIntensity > self.maxVal:
self._nextIntensity = self.maxVal
elif self.minVal is not None and self._nextIntensity < self.minVal:
self._nextIntensity = self.minVal
self._questNextIntensity = self._nextIntensity
From what I understand, this also estimates the mean but corrects it if it is out of bounds. If that is correct, would it make sense to use it instead?
About setting intensities: in the description of the Weibull distribution, is said that the physical units (e.g. contrast/gun value) are in log10 units. However in the example form psychopy it enters the direct gun values. I did as well and seems to be working just fine. Is that ok= or would be recommendable to work with log10 units ( I have not tested this one yet)
Last but not least, in a 2AFC task, QUEST seems to be quite efficient at maintaining performance around a given value. So far, we have tested only with humans due to the issues mentioned above, but I think it could be a good addition, especially for handling multiple conditions and training monkeys.
Let me know your thoughts.
Cheers,
Jaime