Esempio n. 1
0
 def test_comparison_not_equal_after_iteration(self):
     q1 = data.QuestHandler(0.5, 0.2, pThreshold=0.63, gamma=0.01,
                            nTrials=20, minVal=0, maxVal=1)
     q2 = data.QuestHandler(0.5, 0.2, pThreshold=0.63, gamma=0.01,
                            nTrials=20, minVal=0, maxVal=2)
     q1.__next__()
     q2.__next__()
     assert q1 != q2
    def run_quest_procedure(self,
                            n_trials=10,
                            start=20,
                            start_sd=10,
                            maxvalue=40,
                            beta=3.5,
                            delta=0.01,
                            gamma=0.01,
                            grain=0.01):

        print("RUNNING QUEST\n" \
              "-------------\n")

        self.quest = data.QuestHandler(start,
                                       start_sd,
                                       pThreshold=0.75,
                                       nTrials=n_trials,
                                       minVal=0,
                                       maxVal=maxvalue,
                                       beta=beta,
                                       delta=delta,
                                       gamma=gamma,
                                       grain=grain)

        for n, stim_level in enumerate(self.quest):

            print("Calibration trial {0} / {1}\n" \
                  "Stimulation level = {2}\n" \
                  "-----------------------------".format(n + 1, n_trials, stim_level))

            response = None

            # Repeat if subject doesn't respond
            while response == None:
                trial = ConditioningTrial(self,
                                          n,
                                          True,
                                          max_voltage=stim_level)
                response = trial.run()

            # Add the response
            if response:
                print("Stimulation detected")
            else:
                print("Stimulation not detected")

            if n >= self.config['quest_settings']['n_ignored']:
                self.quest.addResponse(int(response))
                p0 = self.quest.quantile(0.01)
                p25 = self.quest.quantile(.25)
                p50 = self.quest.quantile(.50)
                p75 = self.quest.quantile(.75)
                print("1% detection probability level = {0}\n"
                      "25% detection probability level = {1}\n"
                      "50% detection probability level = {2}\n"
                      "75% detection probability level = {3}\n".format(
                          self.quest.quantile(0.01), self.quest.quantile(.25),
                          self.quest.quantile(.5), self.quest.quantile(.75)))

        return p0, p25, p50, p75
Esempio n. 3
0
    def test_json_dump(self):
        q = data.QuestHandler(0.5, 0.2, pThreshold=0.63, gamma=0.01,
                              nTrials=20, minVal=0, maxVal=1)
        dump = q.saveAsJson()

        q.origin = ''
        assert q == json_tricks.np.loads(dump)
Esempio n. 4
0
 def test_comparison_not_equal(self):
     q1 = data.QuestHandler(0.5,
                            0.2,
                            pThreshold=0.63,
                            gamma=0.01,
                            nTrials=20,
                            minVal=0,
                            maxVal=1)
     q2 = data.QuestHandler(0.5,
                            0.2,
                            pThreshold=0.63,
                            gamma=0.01,
                            nTrials=20,
                            minVal=0,
                            maxVal=2)
     assert q1 != q2
Esempio n. 5
0
    def test_epsilon(self):
        # Values used by Harvey (1986), Table 3.
        beta = 3.5
        gamma = 0
        delta = 0
        epsilon = 0.0571

        # QuestHandler needs this, but it doesn't influence our
        # test. Values chosen arbitrarily.
        startVal = 0.5
        startValSd = 1

        # Estimate the target proportion correct based on epsilon.
        # (The values provided by Harvey (1986) are rounded to two
        #  decimal places and, therefore, too imprecise. So we
        #  have to we calculate it again.)
        def weibull(x, beta, gamma, delta):
            p = delta * gamma + (1 - delta) * (
                1 - (1 - gamma) * np.exp(-10**(beta * x)))
            return p

        p = weibull(x=epsilon, beta=beta, gamma=gamma, delta=delta)

        q = data.QuestHandler(startVal=startVal,
                              startValSd=startValSd,
                              pThreshold=p,
                              beta=beta,
                              gamma=gamma,
                              delta=delta)

        assert np.isclose(q.epsilon, epsilon, atol=1e-4)
Esempio n. 6
0
    def test_QuestHandler(self):
        nTrials = 10
        startVal, minVal, maxVal = 50, 0, 100
        range = maxVal - minVal
        startValSd = 50
        grain = 0.01
        pThreshold = 0.82
        beta, gamma, delta = 3.5, 0.5, 0.01
        stopInterval = None
        method = 'quantile'

        self.stairs = data.QuestHandler(startVal,
                                        startValSd,
                                        pThreshold=pThreshold,
                                        nTrials=nTrials,
                                        stopInterval=stopInterval,
                                        method=method,
                                        beta=beta,
                                        gamma=gamma,
                                        delta=delta,
                                        grain=grain,
                                        range=range,
                                        minVal=minVal,
                                        maxVal=maxVal)

        self.stairs.nReversals = None

        self.responses = makeBasicResponseCycles(cycles=3,
                                                 nCorrect=2,
                                                 nIncorrect=2,
                                                 length=10)

        self.intensities = [
            50, 45.139710407074872, 37.291086503930742, 58.297413127139947,
            80.182967131096547, 75.295251409003527, 71.57627192423783,
            79.881680484036906, 90.712313302815517, 88.265808957695796
        ]

        mean = 86.0772169427
        mode = 80.11
        quantile = 86.3849031085

        self.simulate()
        self.checkSimulationResults()

        assert self.stairs.startVal == startVal
        assert self.stairs.startValSd == startValSd

        assert np.allclose(self.stairs.mean(), mean)
        assert np.allclose(self.stairs.mode(), mode)
        assert np.allclose(self.stairs.quantile(), quantile)

        # Check if the internal grid has the expected dimensions.
        assert len(self.stairs._quest.x) == (maxVal - minVal) / grain + 1
        assert np.allclose(self.stairs._quest.x[1] - self.stairs._quest.x[0],
                           grain)
        assert self.stairs._quest.x[0] == -range / 2
        assert self.stairs._quest.x[-1] == range / 2
Esempio n. 7
0
    def test_json_dump_with_data(self):
        q = data.QuestHandler(0.5, 0.2, pThreshold=0.63, gamma=0.01,
                              nTrials=20, minVal=0, maxVal=1)
        q.addResponse(1)
        q.addOtherData('foo', 'bar')
        dump = q.saveAsJson()

        q.origin = ''
        assert q == json_tricks.np.loads(dump)
Esempio n. 8
0
 def test_json_dump_to_file(self):
     _, path = mkstemp(dir=self.tmp_dir, suffix='.json')
     q = data.QuestHandler(0.5,
                           0.2,
                           pThreshold=0.63,
                           gamma=0.01,
                           nTrials=20,
                           minVal=0,
                           maxVal=1)
     q.saveAsJson(fileName=path, fileCollisionMethod='overwrite')
Esempio n. 9
0
 def get_blocks(self):
     blocks = []
     for name in self.oris.keys():
         blocks.append(
             data.QuestHandler(15,
                               3,
                               pThreshold=.63,
                               nTrials=self.ntrials_diff,
                               minVal=0,
                               maxVal=25,
                               name=name))
     self.blocks = blocks
Esempio n. 10
0
    def test_json_dump_and_reopen_file(self):
        q = data.QuestHandler(0.5, 0.2, pThreshold=0.63, gamma=0.01,
                              nTrials=20, minVal=0, maxVal=1)
        q.addResponse(1)
        q.addOtherData('foo', 'bar')
        q.__next__()

        _, path = mkstemp(dir=self.tmp_dir, suffix='.json')
        q.saveAsJson(fileName=path, fileCollisionMethod='overwrite')
        q.origin = ''

        q_loaded = fromFile(path)
        assert q == q_loaded
Esempio n. 11
0
    def test_attributes(self):
        beta = 3.5
        gamma = 0.01
        delta = 0.02
        grain = 0.01
        range = 1

        q = data.QuestHandler(startVal=0.5,
                              startValSd=0.2,
                              pThreshold=0.63,
                              nTrials=10,
                              beta=beta,
                              gamma=gamma,
                              delta=delta,
                              grain=grain,
                              range=range)

        assert q.beta == beta
        assert q.gamma == gamma
        assert q.delta == delta
        assert q.grain == grain
        assert q.range == range
    def run_session(self):

        path = os.path.join(self.res_dir, self.subject)
        if not os.path.exists(path):
            os.makedirs(path)

        # welcome
        msg = visual.TextStim(self.win,
                              'Welcome!' + '\n' +
                              ' Press any key to start this session :)',
                              color='black',
                              units='deg',
                              pos=(0, 0),
                              height=0.8)
        msg.draw()
        self.win.mouseVisible = False
        self.win.flip()
        event.waitKeys()

        # read staircase parameters
        conditions = [
            dict({'stimulus': key}, **value)
            for key, value in self.param.items() if key.startswith('stimulus')
        ]

        if conditions[0]['stairType'] == 'simple':
            stairs = data.MultiStairHandler(stairType='simple',
                                            conditions=conditions,
                                            nTrials=self.trial_nmb,
                                            method='sequential')
        elif conditions[0]['stairType'] == 'quest':
            stairs = []
            for cond in conditions:
                if self.priors_file_path:
                    prior_file = self.priors_file_path + cond[
                        'label'] + '.psydat'
                    print(prior_file)
                    prior_handler = misc.fromFile(prior_file)
                else:
                    prior_handler = None
                cur_handler = data.QuestHandler(cond['startVal'],
                                                cond['startValSd'],
                                                pThreshold=cond['pThreshold'],
                                                nTrials=self.trial_nmb,
                                                minVal=cond['min_val'],
                                                maxVal=cond['max_val'],
                                                staircase=prior_handler,
                                                extraInfo=cond)
                stairs.append(cur_handler)
        elif conditions[0]['stairType'] == 'psi':
            stairs = []
            for cond in conditions:
                if self.priors_file_path:
                    prior_file = self.priors_file_path + cond['label'] + '.npy'
                else:
                    prior_file = None
                print(prior_file)
                cur_handler = data.PsiHandler(nTrials=self.trial_nmb,
                                              intensRange=[1, 10],
                                              alphaRange=[1, 10],
                                              betaRange=[0.01, 10],
                                              intensPrecision=0.1,
                                              alphaPrecision=0.1,
                                              betaPrecision=0.01,
                                              delta=0.01,
                                              extraInfo=cond,
                                              prior=prior_file,
                                              fromFile=(prior_file
                                                        is not None))
                stairs.append(cur_handler)

        # write configuration files
        xpp = config_tools.WriteXpp(self.subject, self.idx)
        xpp_file = xpp.head(self.cfg_file, self.par_file)
        config_tools.write_xrl(self.subject,
                               cfg_file=self.cfg_file,
                               par_file=self.par_file,
                               xpp_file=xpp_file)

        xlsname = path + '/' + self.idx + self.param[
            'noise_condition'] + '.xlsx'
        """ running staircase """

        if isinstance(stairs, data.MultiStairHandler):
            # start running the staircase using the MultiStairHandler for the up-down method
            count = 0

            for rot, cond in stairs:
                count += 1
                judge, thiskey, trial_time = self.run_trial(rot, cond, count)

                # check whether the theta is valid - if not, the rotation given by staircase should be corrected by
                # realizable values
                valid_theta = np.round(np.load(self.hue_list), decimals=1)

                disp_standard = self.take_closest(
                    valid_theta, cond['standard'])  # theta actually displayed
                stair_test = cond['standard'] + stairs._nextIntensity * (-1)**(
                    cond['label'].endswith('m'))  # theta for staircase
                if stair_test < 0:
                    stair_test += 360
                disp_test = self.take_closest(valid_theta, stair_test)
                disp_intensity = abs(disp_test - disp_standard)
                if disp_intensity > 300:
                    disp_intensity = 360 - (disp_test + disp_standard)
                stairs.addResponse(judge, disp_intensity)

                xpp.task(count, cond, rot, float(disp_intensity), judge,
                         trial_time)

                event.waitKeys(keyList=[
                    thiskey
                ])  # press the response key again to start the next trial

            config_tools.write_xrl(self.subject, xls_file=xlsname)
            stairs.saveAsExcel(xlsname)  # save results
            psydat_file_path = os.path.join(
                path, "psydat", self.idx + self.param['condition'] +
                '.psydat')  # save the handler into a psydat-file
            misc.toFile(psydat_file_path, stairs)

        elif isinstance(stairs, list):
            # start running the staircase using custom interleaving stairs for the quest and psi methods
            count = 0
            rot_all = []
            rot_all_disp = []
            judge_all = []
            estimates = {s.extraInfo['label']: [] for s in stairs}

            for trial_n in range(self.trial_nmb):
                for handler_idx, cur_handler in enumerate(stairs):
                    count += 1
                    rot = next(cur_handler)

                    if len(rot_all) <= handler_idx:
                        rot_all.append([])
                    rot_all[handler_idx].append(rot)
                    cond = cur_handler.extraInfo
                    judge, thiskey, trial_time = self.run_trial(
                        rot, cond, count)

                    if len(judge_all) <= handler_idx:
                        judge_all.append([])
                    judge_all[handler_idx].append(judge)
                    # cur_handler.addResponse(judge)  # to the next trial

                    valid_theta = np.round(np.load(self.hue_list), decimals=1)
                    disp_standard = self.take_closest(
                        valid_theta,
                        cond['standard'])  # theta actually displayed
                    stair_test = cond[
                        'standard'] + cur_handler._nextIntensity * (-1)**(
                            cond['label'].endswith('m'))  # theta for staircase
                    if stair_test < 0:
                        stair_test += 360
                    disp_test = self.take_closest(valid_theta, stair_test)
                    disp_intensity = abs(disp_test - disp_standard)
                    if disp_intensity > 300:
                        disp_intensity = 360 - (disp_test + disp_standard)
                    cur_handler.addResponse(judge, disp_intensity)

                    if len(rot_all_disp
                           ) <= handler_idx:  # add displayed intensities
                        rot_all_disp.append([])
                    rot_all_disp[handler_idx].append(disp_intensity)

                    print('stair test: ' + str(stair_test) + ', ' +
                          'disp_test:' + str(disp_test))

                    if isinstance(cur_handler, data.PsiHandler):
                        estimates[cur_handler.extraInfo['label']].append([
                            cur_handler.estimateLambda()[0],  # location
                            cur_handler.estimateLambda768()[1],  # slope
                            cur_handler.estimateThreshold(0.75)
                        ])
                    elif isinstance(cur_handler, data.QuestHandler):
                        estimates[cur_handler.extraInfo['label']].append([
                            cur_handler.mean(),
                            cur_handler.mode(),
                            cur_handler.quantile(0.5)
                        ])

                    xpp.task(count, cond, rot, disp_intensity, judge,
                             trial_time)
                    event.waitKeys(keyList=[
                        thiskey
                    ])  # press the response key again to start the next trial

            config_tools.write_xrl(self.subject, xls_file=xlsname)

            # save results in xls-file
            workbook = xlsxwriter.Workbook(xlsname)
            for handler_idx, cur_handler in enumerate(stairs):
                worksheet = workbook.add_worksheet(
                    cur_handler.extraInfo['label'])
                worksheet.write('A1', 'Reversal Intensities')
                worksheet.write('B1', 'Reversal Indices')
                worksheet.write('C1', 'All Intensities')
                worksheet.write('D1', 'All Responses')
                for i in range(len(rot_all[handler_idx])):
                    # worksheet.write('C' + str(i + 2), rot_all[handler_idx][i])
                    worksheet.write('C' + str(i + 2),
                                    rot_all_disp[handler_idx][i])
                    worksheet.write('D' + str(i + 2),
                                    judge_all[handler_idx][i])
            workbook.close()

            # print resulting parameters and estimates for each step
            res_file_path = os.path.join(path, self.idx + '_estimates.csv')
            res_writer = csv.writer(open(res_file_path, 'w'))
            for res_stim, res_vals in estimates.items():
                for res_val_id, res_val in enumerate(res_vals):
                    res_writer.writerow([
                        res_stim, res_val_id, res_val[0], res_val[1],
                        res_val[2]
                    ])

            # save each handler into a psydat-file and save posterior into a numpy-file
            for cur_handler in stairs:
                file_name = os.path.join(
                    path, self.idx + self.param['noise_condition'] +
                    cur_handler.extraInfo['label'])
                misc.toFile(file_name + '.psydat', cur_handler)
                if isinstance(cur_handler, data.PsiHandler):
                    cur_handler.savePosterior(file_name + '.npy')
        thisStair.addResponse(correct, isoi)
        trialData.write('{},{},{},{}\n' .format(thisStair.extraInfo['Label'],isoi, direction, int(correct)))
    
    print('{} of {} trials complete\n' .format(trialNum+1, nTrials))

print('\n=== EXPERIMENT FINISHED ===\n')
## ----

## -- use quest to estimate threshold --
if exptInfo['09. Number of trials per staircase'] > 0:
    for s in staircases:
        s.saveAsPickle(fileName+'_'+s.extraInfo['Label']) #special python binary file to save all the info
        q = data.QuestHandler(log(exptInfo['06. First ISOI (ms)']), 
                                log(exptInfo['06. First ISOI (ms)']), 
                                pThreshold = 0.82, 
                                nTrials = exptInfo['09. Number of trials per staircase'],
                                grain=0.1, 
                                range=10,
                                minVal = log(exptInfo['11. Min ISOI (ms)']), 
                                maxVal = log(exptInfo['12. Max ISOI (ms)']))
        isois = [log(i) for i in s.intensities]
        q.importData(isois,s.data)
        threshold = exp(q.mean())
        tSD = exp(q.sd())
        print('\n'+s.extraInfo['Label'])
        print('threshold: {}' .format(threshold))
        print('sd: {}' .format(tSD))

        thresholdData.write('{},{},{},{},{},{},{},{},{},{}\n' .format(threshold,tSD,
                            s.extraInfo['Label'],
                            s.extraInfo['direction'],
                            exptInfo['02. Test number'],
Esempio n. 14
0
 def test_json_dump_to_file(self):
     q = data.QuestHandler(0.5, 0.2, pThreshold=0.63, gamma=0.01,
                           nTrials=20, minVal=0, maxVal=1)
     q.saveAsJson(fileName=self.tmp_dir, fileCollisionMethod='overwrite')
    def run_session(self):

        path = os.path.join(self.res_dir, self.subject)
        if not os.path.exists(path):
            os.makedirs(path)
        psydat_path = os.path.join(path, 'psydat')
        if not os.path.exists(psydat_path):
            os.makedirs(psydat_path)

        # welcome
        msg = visual.TextStim(self.win,
                              'Welcome!' + '\n' +
                              ' Press any key to start this session :)',
                              color='black',
                              units='deg',
                              pos=(0, 0),
                              height=0.8)
        msg.draw()
        self.win.mouseVisible = False
        self.win.flip()
        event.waitKeys()

        # read staircase parameters
        conditions = [
            dict({'stimulus': key}, **value)
            for key, value in self.param.items() if key.startswith('stimulus')
        ]

        if conditions[0]['stairType'] == 'simple':
            stairs = data.MultiStairHandler(stairType='simple',
                                            conditions=conditions,
                                            nTrials=self.trial_nmb,
                                            method='sequential')
        elif conditions[0]['stairType'] == 'quest':
            stairs = []
            for cond in conditions:
                if self.priors_file_path:
                    prior_file = self.priors_file_path + cond[
                        'label'] + '.psydat'
                    print(prior_file)
                    prior_handler = misc.fromFile(prior_file)
                else:
                    prior_handler = None
                cur_handler = data.QuestHandler(cond['startVal'],
                                                cond['startValSd'],
                                                pThreshold=cond['pThreshold'],
                                                nTrials=self.trial_nmb,
                                                minVal=cond['min_val'],
                                                maxVal=cond['max_val'],
                                                staircase=prior_handler,
                                                extraInfo=cond,
                                                grain=0.02)
                stairs.append(cur_handler)
        elif conditions[0]['stairType'] == 'psi':
            stairs = []
            for cond in conditions:
                if self.priors_file_path:
                    prior_file = self.priors_file_path + cond['label'] + '.npy'
                else:
                    prior_file = None
                print(prior_file)
                cur_handler = data.PsiHandler(nTrials=self.trial_nmb,
                                              intensRange=[1, 10],
                                              alphaRange=[1, 10],
                                              betaRange=[0.01, 10],
                                              intensPrecision=0.1,
                                              alphaPrecision=0.1,
                                              betaPrecision=0.01,
                                              delta=0.01,
                                              extraInfo=cond,
                                              prior=prior_file,
                                              fromFile=(prior_file
                                                        is not None))
                stairs.append(cur_handler)

        # write configuration files
        xpp = config_tools.WriteXpp(self.subject, self.idx)
        xpp_file = xpp.head(self.cfg_file, self.par_file)
        config_tools.write_xrl(self.subject,
                               cfg_file=self.cfg_file,
                               par_file=self.par_file,
                               xpp_file=xpp_file)

        xlsname = path + '/' + self.idx + self.param[
            'noise_condition'] + '.xlsx'
        """ running staircase """

        if isinstance(stairs, data.MultiStairHandler):
            # start running the staircase using the MultiStairHandler for the up-down method
            count = 0

            for rot, cond in stairs:
                count += 1
                direction = (-1)**(cond['label'].endswith('m')
                                   )  # direction as -1 if for minus stim
                rot = rot * direction  # rotation for this trial
                judge, react_time, trial_time_start = self.run_trial(
                    rot, cond, cond['std'], count)

                # check whether the theta is valid - if not, the rotation given by staircase should be corrected by
                # realizable values
                valid_theta = np.round(np.load(self.hue_list), decimals=1)

                disp_standard = self.take_closest(
                    valid_theta, cond['standard'])  # theta actually displayed
                stair_test = cond[
                    'standard'] + stairs._nextIntensity * direction
                if stair_test < 0:
                    stair_test += 360
                disp_test = self.take_closest(valid_theta, stair_test)
                disp_intensity = disp_test - disp_standard
                if disp_intensity > 300:
                    disp_intensity = (disp_test + disp_standard) - 360
                stairs.addResponse(judge, abs(disp_intensity))

                xpp.task(count, cond, rot, float(disp_intensity), judge,
                         react_time, trial_time_start)

                if 'escape' in event.waitKeys():
                    config_tools.write_xrl(self.subject,
                                           break_info='userbreak')
                    core.quit()

            config_tools.write_xrl(self.subject, xls_file=xlsname)
            stairs.saveAsExcel(xlsname)  # save results
            misc.toFile(
                os.path.join(
                    psydat_path,
                    self.idx + self.param['noise_condition'] + '.psydat'),
                stairs)

        elif isinstance(stairs, list):
            # start running the staircase using custom interleaving stairs for the quest and psi methods
            count = 0
            rot_all = []
            rot_all_disp = []
            judge_all = []
            estimates = {s.extraInfo['label']: [] for s in stairs}

            for trial_n in range(self.trial_nmb):
                for handler_idx, cur_handler in enumerate(stairs):
                    count += 1
                    direction = (-1)**(
                        cur_handler.extraInfo['label'].endswith('m')
                    )  # direction as -1 if for minus stim

                    if cur_handler._nextIntensity >= 10.0:
                        sys.exit(
                            "Hue difference is out of range! Please enlarge the testing range or take more training!"
                        )

                    rot = cur_handler._nextIntensity * direction  # rotation for this trial
                    if trial_n >= 5:  # avoid repeating an intensity more than 3 times
                        last_rots = [
                            np.round(r, decimals=1) for r in [
                                rot_all_disp[handler_idx][trial_n - 1],
                                rot_all_disp[handler_idx][trial_n - 2],
                                rot_all_disp[handler_idx][trial_n - 3]
                            ]
                        ]
                        last_resp = [
                            judge_all[handler_idx][trial_n - 1],
                            judge_all[handler_idx][trial_n - 2],
                            judge_all[handler_idx][trial_n - 3]
                        ]
                        if last_rots[0] == last_rots[1] == last_rots[2] \
                                and last_resp[0] == last_resp[1] == last_resp[2]:
                            if cur_handler._nextIntensity > 0.5:
                                rot = (cur_handler._nextIntensity -
                                       0.5) * direction
                                print('Intensity decreases by 0.5!')
                            if cur_handler._nextIntensity <= 0.5:
                                rot = (cur_handler._nextIntensity +
                                       0.5) * direction
                                print('Intensity increases by 0.5!')
                    cond = cur_handler.extraInfo
                    judge, react_time, trial_time_start = self.run_trial(
                        rot, cond, cond['std'], count)

                    if len(rot_all) <= handler_idx:
                        rot_all.append([])
                    rot_all[handler_idx].append(rot)

                    if len(judge_all) <= handler_idx:
                        judge_all.append([])
                    judge_all[handler_idx].append(judge)

                    valid_theta = np.round(np.load(self.hue_list), decimals=1)
                    disp_standard = self.take_closest(valid_theta,
                                                      cond['standard'])
                    stair_test = cond[
                        'standard'] + rot  # calculated test hue for this trial
                    if stair_test < 0:
                        stair_test += 360
                    disp_test = self.take_closest(
                        valid_theta,
                        stair_test)  # actual displayed test hue for this trial

                    disp_intensity = disp_test - disp_standard  # actual displayed hue difference
                    if disp_intensity > 300:
                        disp_intensity = (disp_test + disp_standard) - 360

                    cur_handler.addResponse(
                        judge, abs(disp_intensity)
                    )  # only positive number is accepted by addResponse

                    if len(rot_all_disp
                           ) <= handler_idx:  # add displayed intensities
                        rot_all_disp.append([])
                    rot_all_disp[handler_idx].append(disp_intensity)

                    if isinstance(cur_handler, data.PsiHandler):
                        estimates[cur_handler.extraInfo['label']].append([
                            cur_handler.estimateLambda()[0],  # location
                            cur_handler.estimateLambda768()[1],  # slope
                            cur_handler.estimateThreshold(0.75)
                        ])
                    elif isinstance(cur_handler, data.QuestHandler):
                        estimates[cur_handler.extraInfo['label']].append([
                            cur_handler.mean(),
                            cur_handler.mode(),
                            cur_handler.quantile(0.5)
                        ])

                    xpp.task(count, cond, rot, disp_intensity, judge,
                             react_time, trial_time_start)

                    if 'escape' in event.waitKeys():
                        config_tools.write_xrl(self.subject,
                                               break_info='userbreak')
                        core.quit()

            config_tools.write_xrl(self.subject, xls_file=xlsname)

            # save results in xls-file
            workbook = xlsxwriter.Workbook(xlsname)
            for handler_idx, cur_handler in enumerate(stairs):
                worksheet = workbook.add_worksheet(
                    cur_handler.extraInfo['label'])
                worksheet.write('A1', 'Reversal Intensities')
                worksheet.write('B1', 'Reversal Indices')
                worksheet.write('C1', 'All Intensities')
                worksheet.write('D1', 'All Responses')
                for i in range(len(rot_all[handler_idx])):
                    # worksheet.write('C' + str(i + 2), rot_all[handler_idx][i])
                    worksheet.write('C' + str(i + 2),
                                    rot_all_disp[handler_idx][i])
                    worksheet.write('D' + str(i + 2),
                                    judge_all[handler_idx][i])
            workbook.close()

            # print resulting parameters and estimates for each step
            res_file_path = os.path.join(path, self.idx + '_estimates.csv')
            res_writer = csv.writer(open(res_file_path, 'w'))
            for res_stim, res_vals in estimates.items():
                for res_val_id, res_val in enumerate(res_vals):
                    res_writer.writerow([
                        res_stim, res_val_id, res_val[0], res_val[1],
                        res_val[2]
                    ])

            # save each handler into a psydat-file and save posterior into a numpy-file
            for cur_handler in stairs:
                file_name = os.path.join(
                    psydat_path, self.idx + self.param['noise_condition'] +
                    cur_handler.extraInfo['label'])
                misc.toFile(file_name + '.psydat', cur_handler)
                if isinstance(cur_handler, data.PsiHandler):
                    cur_handler.savePosterior(file_name + '.npy')
        ax2.tick_params(axis='x',which='minor',bottom='off')
        
#    #save figure to file
#    outputFile = os.path.join(dataDir, 'test.pdf')
#    pylab.savefig(outputFile)

if __name__ == "__main__":
    #Test staircase functions
    threshCriterion = 0.25
    staircaseTrials = 5
    staircase = data.QuestHandler(startVal = 95, 
                          startValSd = 80,
                          stopInterval= 1, #sd of posterior has to be this small or smaller for staircase to stop, unless nTrials reached
                          nTrials = staircaseTrials,
                          #extraInfo = thisInfo,
                          pThreshold = threshCriterion, #0.25,    
                          gamma = 1./26,
                          delta=0.02, #lapse rate, I suppose for Weibull function fit
                          method = 'quantile', #uses the median of the posterior as the final answer
                          stepType = 'log',  #will home in on the 80% threshold. But stepType = 'log' doesn't usually work
                          minVal=1, maxVal = 100
                          )
    print('created QUEST staircase')
    
    descendingPsycho = False
    noiseEachTrial = np.array([5,5,5,5,5,5,5,5,5,10,10,10,10,10,10,10,10,10,10,10,20,20,20,20,20,20,20,20,20,20,20,20,50,50,50,50,50,50,50,60,60,60,60,60,60,60,60,60,60,70,70,70,70,70,70,70,80,80,80,80,80,80,80,80,95,95,95,95,95,95,95]) 
    centeredOnZero = noiseEachTrial/100. -0.5
    guessRate = .1  #doesnt work with guessRate=0, fitWeibull doesnt like that
    pCorrEachTrial = guessRate*.5 + (1-guessRate)* 1. / (1. + np.exp(-20*centeredOnZero)) #sigmoidal probability

    print('pCorrEachTrial=',np.around(pCorrEachTrial,2))
    corrEachTrial = np.zeros( len(noiseEachTrial) )
screen.setSizePix([1680, 1050])
screen.setWidth(47.475)
screen.setDistance(57)
win = visual.Window(allowGUI=True, units='deg', monitor=screen)

grating = visual.GratingStim(win,
                             tex='sin',
                             mask='gauss',
                             contrast=1,
                             sf=3,
                             size=3)

staircase = data.QuestHandler(0.5,
                              0.2,
                              pThreshold=0.63,
                              gamma=0.01,
                              minVal=0,
                              maxVal=1,
                              ntrials=10)

orientations = [-45, 45]
responses = ['left', 'right']

for contrast in staircase:
    keys = []
    # randomise orientation for this trial
    grating.ori = np.random.choice(orientations)
    # update the difficulty (the contrast)
    grating.contrast = contrast
    # before the trial: wait 500ms
    core.wait(0.5)
Esempio n. 18
0
    for thisComponent in instrPracticeComponents:
        if hasattr(thisComponent, "setAutoDraw"):
            thisComponent.setAutoDraw(False)
    # check responses
    if ok1.keys in ['', [], None]:  # No response was made
        ok1.keys=None
    thisExp.nextEntry()
    # the Routine "instrPractice" was not non-slip safe, so reset the non-slip timer
    routineTimer.reset()


# --------Prepare to start Staircase "trials" --------
# set up handler to look after next chosen value etc
if expInfo['participant'] == 'ideal' :
    trials = data.QuestHandler(startVal = 0.1, 
                      startValSd = 0.05, delta=0,
                      pThreshold = 0.63, 
                      nTrials=1E3, minVal=0, maxVal=1)
else :
    trials = data.QuestHandler(startVal = 0.5, 
                      startValSd = 0.2, 
                      pThreshold = 0.63, 
                      nTrials=50, minVal=0, maxVal=1)
   
thisExp.addLoop(trials)  # add the loop to the experiment
level = thisTrial = 0.5  # initialise some vals

face = visual.ImageStim(win, happy_face)
key_resp_2 = event.BuilderKeyResponse()
for thisTrial in trials:
    currentLoop = trials
    level = thisTrial
Esempio n. 19
0
def runQuest(experimentStructure, stimuliStructure, data, feedbackText,
             pThreshold, questTrials):
    """ This function implements a Quest procedure to individualize the maximum contrast difference
        
        Currently not in use.
        
        Input:
            exerimentStructure: all general experimental properties 
            stimuliStructure: all general stimulus properties
            data: PsychoPy data functions
            feedbackText: feedback text object instance
            pThreshold: threshold for quest procedure
            questTrials: number of trials for qQest procedure
        
        Return: 
            trials.quantile: quantile of Quest posterior pdf
            accPerf: accumulated performance of current block
    """

    # Create some shortnames
    thisExp = experimentStructure['thisExp']
    patch1 = stimuliStructure['patch1']
    patch2 = stimuliStructure['patch2']
    questClock = stimuliStructure['questClock']

    # Create some info to store with the data
    info = {}
    info['startPoints'] = [0.03, 0.04]
    info['nTrials'] = questTrials
    lowerBound = 0.01
    upperBound = 0.05

    whichLoop = 'quest'

    stairs = []
    for thisStart in info['startPoints']:

        # We need a COPY of the info for each staircase
        # (or the changes here will be made to all the other staircases)
        thisInfo = copy.copy(info)
        # Now add any specific info for this staircase
        thisInfo['thisStart'] = thisStart  #we might want to keep track of this
        trials = data.QuestHandler(startVal=thisStart,
                                   startValSd=0.1,
                                   extraInfo=thisInfo,
                                   pThreshold=pThreshold,
                                   nTrials=questTrials,
                                   minVal=lowerBound,
                                   maxVal=upperBound,
                                   name='quest',
                                   gamma=0.5)
        thisExp.addLoop(trials)
        stairs.append(trials)

    accPerf = 0.0
    for trialN in range(info['nTrials']):
        for trials in stairs:
            thisDifference = trials.next()
            print 'start=%.2f, current=%.4f' % (trials.extraInfo['thisStart'],
                                                thisDifference)
            lowerBound = float('NaN')
            upperBound = float('NaN')

            positions = np.random.choice([0, 1])
            if positions == 1:
                targetPatch = 'left'
            else:
                targetPatch = 'right'

            (decision1, trials) = createPatches(experimentStructure,
                                                stimuliStructure, questClock,
                                                lowerBound, upperBound,
                                                targetPatch, trials, whichLoop,
                                                thisDifference)

            if (decision1.keys == None):

                msg = 'Zu langsam!'
                giveFeedback(experimentStructure, stimuliStructure,
                             feedbackText, msg)

            if decision1.corr:
                accPerf = accPerf + 1

            thisExp.nextEntry()

    questQuantiles = []
    for trials in stairs:
        questQuantiles.append(trials.quantile())
        print(questQuantiles)

    print(np.mean(questQuantiles))
    return (trials.quantile(), accPerf)
Esempio n. 20
0
def main():
    # Sets system time as the random seed
    np.random.seed(seed=None)

    # Prompt GUI for participant ID
    id = ask_for_id()

    # Set up window and how many frames image should be displayed depending on refresh rate of monitor
    window = visual.Window(color=bg_color,
                           monitor='monitor',
                           fullscr=full_screen)
    frame_rate = window.getActualFrameRate()

    # Display instructional messages for user
    instruction_msg = visual.TextStim(
        window,
        color=text_color,
        text=
        f"For each image, answer whether the building shown is damaged. Each image will be shown briefly.\n\nTo answer, please press {key_list[0]} for yes and {key_list[1]} for no.\n\nPress any key to continue."
    )
    instruction_msg.draw()
    window.flip()
    event.waitKeys()
    instruction_msg.text = f"For the first few images, you will hear a beep if you give the correct answer.\n\nPress any key to continue."
    instruction_msg.draw()
    window.flip()
    event.waitKeys()

    plus_horiz = visual.ShapeStim(window,
                                  units='pix',
                                  vertices=[[-20, 0], [20, 0]])
    plus_vert = visual.ShapeStim(window,
                                 units='pix',
                                 vertices=[[0, 20], [0, -20]])

    user_data = pd.DataFrame(columns=data_columns)
    img = visual.ImageStim(window, size=img_dims)

    # Setup staircase procedures using defined parameters
    conds = data.importConditions('demo_params.csv')
    staircases = []
    for cond in conds:
        staircases.append(
            data.QuestHandler(startVal=cond['startVal'],
                              startValSd=cond['startValSd'],
                              pThreshold=cond['pThreshold'],
                              nTrials=cond['nTrials'],
                              beta=cond['beta'],
                              delta=cond['delta'],
                              gamma=cond['gamma'],
                              minVal=cond['minVal'],
                              maxVal=cond['maxVal']))

    img_list = get_imgs(img_dir, damage_subdir, num_each, nodamage_subdir)
    print(f"{len(img_list)} images loaded.")

    key_list.append('escape')

    # Run through image list with participant
    rounds = 1
    while len(img_list) > 0:

        staircase = random.choice(staircases)
        curr_time = next(staircase)
        curr_time = float(format(curr_time, '.3f'))

        # Get and parse random image's information
        subdir, img_name = get_random_img(img_list)
        truth = 'y' if subdir == damage_subdir else 'n'
        img.setImage(img_dir + subdir + '/' + img_name)

        # Display the image for set number of frames
        frames_to_show = get_frames_to_show(curr_time, frame_rate)
        keypress = None
        start_time, end_time = time.time(), 0
        for _ in range(frames_to_show):
            img.draw()
            window.flip()

            keypress = event.getKeys(keyList=key_list)
            if 'escape' in keypress:
                instruction_msg.text = 'Experiment aborted. Quitting...'
                instruction_msg.draw()
                window.flip()
                core.wait(3.0)
                core.quit()
            if len(keypress) > 0:
                end_time = time.time()
                break

        plus_horiz.draw()
        plus_vert.draw()
        window.flip()

        # Track reaction time for user response
        if keypress is None or len(keypress) == 0:
            keypress = event.waitKeys(keyList=key_list)
            if 'escape' in keypress:
                instruction_msg.text = 'Experiment aborted. Quitting...'
                instruction_msg.draw()
                window.flip()
                core.wait(3.0)
                core.quit()
            end_time = time.time()
        reaction_time = float(format(end_time - start_time, '.3f'))

        # Log and process user's input as correct or incorrect
        answer = 'y' if keypress[0] == key_list[0] else 'n'
        event.clearEvents()
        if answer == truth:
            if rounds <= feedback_rounds:
                beep = sound.Sound('A', secs=0.25)
                beep.play()
            staircase.addResponse(1)
        else:
            staircase.addResponse(0)

        user_data.loc[len(user_data)] = ([
            img_name, answer, truth, curr_time, reaction_time
        ])
        rounds += 1

        if rounds == feedback_rounds + 1:
            instruction_msg.text = 'From now on, feedback will no longer be provided.'
            instruction_msg.draw()
            window.flip()
            core.wait(3.0)

    # Calculate the final presentation time from the average of the staircases
    avg = 0
    for staircase in staircases:
        avg += next(staircase)
    avg /= len(staircases)
    with open('demo_result.txt', 'w') as f:
        f.write(f"The final presentation time is {avg} seconds.\n")

    user_data['Correct'] = (
        user_data['Actual'] == user_data['Response']).astype(int)

    # Output individual participant data to .csv file
    if not os.path.exists(data_dir):
        os.makedirs(data_dir)
    user_data.to_csv(f"{data_dir}/data_{id}.csv")

    instruction_msg.text = "Test completed. Closing window..."
    instruction_msg.draw()
    window.flip()
    core.wait(3.0)

    core.quit()
Esempio n. 21
0
    'observer': subject_initials,
}
expInfo['dateStr'] = data.getDateStr()  # add the current time

# make a text file to save data
fileName = expInfo['observer'] + expInfo['dateStr']
dataFile = open(fileName + '.csv',
                'w')  # a simple text file with 'comma-separated-values'
dataFile.write('targetSide,volume,correct\n')

# create the staircase handler
staircase = data.QuestHandler(startVal=0.5,
                              startValSd=0.05,
                              pThreshold=0.65,
                              gamma=0.5,
                              beta=3.5,
                              delta=0.01,
                              nTrials=nr_trials,
                              minVal=0,
                              maxVal=1)

# create window and stimuli
win = visual.Window([1920, 1080],
                    fullscr=True,
                    allowGUI=True,
                    monitor='testMonitor',
                    units='pixels')
fixation = visual.GratingStim(win,
                              pos=(0, 0),
                              tex='sin',
                              mask='circle',
Esempio n. 22
0
    gen_concentration_steps()[exp_info['Substance']])

start_val = np.log10(get_start_val(exp_info['Substance']))
quest_params = dict(
    startVal=start_val,
    startValSd=np.log10(20),
    pThreshold=0.82,
    beta=3.5,
    gamma=0.01,
    delta=0.01,
    grain=0.01,
    range=2 * np.abs(concentration_steps.max() - concentration_steps.min()),
    nTrials=20,
    stopInterval=None)

quest_handler = data.QuestHandler(**quest_params)
exp_handler = data.ExperimentHandler(extraInfo=exp_info,
                                     savePickle=True,
                                     saveWideText=True,
                                     dataFileName=os.path.join(
                                         path['rawdata_dir'],
                                         path['base_filename']))
exp_handler.addLoop(quest_handler)

for concentration_proposed in quest_handler:
    trial_number = quest_handler.thisTrialN + 1  # QuestHandler counts from 0.

    # QUEST proposes a concentration to present,
    # but we usually don't have that particular one prepared.
    #
    # So we pick the one from our set which is closest to the proposed.