Choice Titration Experiment

Today we will program part of an Indifference Point elicitation task, as reported in Experiment 2 Weber & Chapman (2005). In this type of task the item that participants see on the next trial is generated based on the history of responses they have made. This process is used to try and find stimuli that meet a certain criteria (for instance to find stimuli where the participant has no preference between them). In Experiment 2 of Weber & Chapman (2005), they use a bisection titration method to generate stimuli in order to a certain amount of money which the participant considers equivalent to a fixed risky reward. There are a variety of other methods you could use to generate stimuli adaptively, and a variety of tasks where these processes are used. For instance they are widely used in the psychophysics literature ( Treutwein, 1995).

The key/new tasks you will learn this week are:

  1. How to present two stimuli at the same time.
  2. How to calculate variables based on participants responses.
  3. How to present user defined variables.
  4. How to conditionally skip trials/end a trial sequence.

As with last week we are going to simplify the design of Experiment 2. In particular we are only going to create a single condition, the one which is given in the example.

Exercises

Like last week we are going to start a new Inquisit file for this weeks exercises. Call this file surname_wpa5.iqx. This is the file you will send me at the end of the class. I strongly recommend that you copy the defaults element from last weeks script into the top of this file, so that you have an easy way to terminate the Experiment.

1. Present Placeholder Stimuli

Before we cover the more complicated aspects of this Experiment setup, we’ll create the broad structure of the Experiment. Broadly speaking, all we need to do in this Experiment is present two pieces of text on the screen, one on the left and one on the right, and ask participants to choose between them. For now just create an Experiment that presents “100% chance of $1500” on the left of the screen, and “10% chance of $3000”, on the right of the screen. We’ll then have participants respond left or right using the A and L keys. Later one we’ll work out how to make the left stimuli change based on participants responses.

a. Create two text stimuli

First create your two text stimuli, one for each side of the screen. Call the left one safe and the right one risky. You should be able to do this easily by now. If you are struggling call me over.

Some questions to consider: 1) Do you need to create any item elements? 2) How do you make sure one is displayed on the left and the other the right?

1) No. We want each text element to display the same piece of information each trial (at the moment), so we can just give it the text string directly in the items attributes. 
2) Remember that we can define the position where stimuli are displayed as a percentage of the screen 0%, 0% displays in the top left of the screen and 100%, 100% in the bottom right.

<text risky>
/items=("10% Chance of $3000 now") 
 /position = (80%, 50%)
 /hjustify = center
 /fontstyle=("Arial", 4%)
</text>


<text safe>
/items=("100% Chance of $1500 now") 
 /position = (20%, 50%)
 /hjustify = center
 /fontstyle=("Arial", 4%)
</text>

b. Create the trial

i. One Stimuli

Now we want to create the trial where these stimuli will be displayed. First create the trial such that only the safe stimuli is displayed, and a left/right response is collected using the A/L keys. Display the stimuli at the start of the trial, but place a pause of 500 ms between trials. If you are unsure how to do this, call me over, or check out the hint.

Hint:

If you want to create a trial, first youll need to create a trial element. There are several attributes of this trial element you then need to define.
1) stimulustimes. This attribute tells Inquisit what stimuli (e.g. text) elements to dsiplay during the trial and when they should be displayed.
2) pretrialpause. The name is already a hint.
3) validresponse. Specify which response Inquisit will record.

Answer:

<trial risk>
 /stimulustimes = [0=safe]
 /validresponse = ("a", "l")
/pretrialpause = 500
</trial>
ii. Two Stimuli

Now alter your trial element so that the second stimuli (i.e. the risky stimuli) is displayed. We learnt last week that if specify multiple stimuli at different times in the stimulustimes attribute they will each be added to the screen at the specified time, but not erased from the screen until the end of the trial (although earlier stimuli can be obscured by later stimuli). So if we want to have two stimuli added to the screen at the same time, what do you think you should do?

Its as easy as just giving them the same start time. We already did this last week with the blank stimuli we used to obscure earlier stimuli.


<trial risk>
 /stimulustimes = [0=safe; 0=risky]
 /validresponse = ("a", "l")
/pretrialpause = 500
</trial>

c. Multiple Trials

Finally, we don’t wantto just present this trial once, we want to present it multiple times (lets start with 15 times). You should have an idea how to do this from previous weeks, but if you need some help, check out the hint, or call me over. Hint:

Remember that if we are defining how multiple trials should link to each other (or a trial should be repeated) we are defining what happens in a block of the Experiment. Therefore we need to define a block element.

Further hint:
The main attribute that block elements have is the trials attribute, which works similar to the stimulustimes attribute of the trial element. It defines which trials should be displayed when.

Answer:

<block risk>
/trials =[1-15 = risk]
</block>

2. Custom Values/Variables

We spoke last week about how Inquisit records various variables/values associated with elements that are presented, which you can access directly in your script. For instance, if you wanted to know how long it took participants to respond on the previous trial then you’d want to look at the variable trial.risk.latency, which stores the response time (or latency) for the trial element named risk, from the last ime it was displayed (assuming you used the same name for you trial element as me).

In addition to these element linked variables, you can also create custom variables that are accessible in your script. Just like the stanard variables these custom variables can be called in your script or updated based on participants responses, although you define directly how they are updated. You can also display these variables on the screen as part of text strings (you can also do this with the standard variables like trial.risk.latency). These variables are similar to global variables in other languages.

a. Displaying a variable as part of a string

First we’ll replace the $1500 that is displayed on every trial as part of the safe option, with instead the lateny from the previous trial. This is just to demonstate how we can display these variables. Because we are changing what text is being displayed, to implement this change we need to look at the items attribute of our text element. The tricky (but actually very easy) part is how to tell Inquisit that you want it to display “100% chance of $trial.risk.latency”, but with trial.risk.latency replaced by the value stored here. What happens if you just replace the current text string with this one?

Youll just see the words "100% chance of $trial.risk.latency" displayedon the left of the screen every trial.

What you need to do is to tell Inquisit that rather than treating trial.risk.latency as text, it should instead interpret it as an Expression. Put simply an expression is a mathematical/logical operation that Inquisit should perform. For trial.risk.latency by labelling this text as an expression Inquisit will treat it like a variable, and call its value, rather than as text. Have a look at the helpfile on expressions. Near the bottom, under the Stimulus Items heading you will see an example that matches exactly what we are trying to do. In essence if you want to call an expression inside a text string, you need to place the expression between <% and %>.

<text safe>
/items=("100% Chance of $<%trial.risk.latency%> now") 
 /position = (20%, 50%)
 /hjustify = center
 /fontstyle=("Arial", 4%)
</text>

Run your Experiment again and see what happens.

b. Creating a custom variable

Rather than use a variable that Inquisit automatically updates, we want to create a variable we can directly control. This is very straightforward. Just like we can define at the start of our Experiment a some default settings (i.e. the defaults element), we can also define custom values that we are going to use throughout. This is done by creating an element called values. Each attribute in this element is then just the name of a custom value you want to create, and the initial value that custom value should take. Try to create a custom variable/value called current. We’ll use this variable to determine what amount of money should be displayed each trial. Set its initial value to 1500.

<values>
/current=1500
</values>

Replace the response latency in your text string with the value of the custom variable called current. To call the value of current use the same naming convention you use for variables attached to trial or other elements. In particular the name of this variable would be values.current as it is an attribute of the values element, which does not itself have a name.

<text safe>
/items=("100% Chance of $<%values.current%> now") 
 /position = (20%, 50%)
 /hjustify = center
 /fontstyle=("Arial", 4%)
</text>

c. Updating a custom variable

We now have a static value of 1500 that is displayed on every trial. The next step is to change this value at the end of each trial. To start with, we’ll just increase this value by 50 after each trial. Inquisit has two attributes, at the trial level, which alow you to define actions which should be undertaken at either the start (ontrialbegin), or the end (ontrialend) of the trial. Each of these attributes takes a list of expressions as its input (the same way stimulutimes takes a list of stimuli with associated dsiplay times). The following expression would add 50 to the current value values.current = values.current +50. Try implementing it using ontrialend. Look at the expression or ontrialend helpfiles.

<trial risk>
/ontrialend = [values.current = values.current+50] 
 /stimulustimes = [0=safe; 0=risky]
 /validresponse = ("a", "l")
/pretrialpause = 500
</trial>

If you have done it correctly the value displayed on the left of the screen should start at 1500 and increase on each trial.

i. Conditional Updating

Rather than having it increase regardless of the response, as the first step in programming the actual titration, we’ll instead have the displayed value increase by 50 if they choose the risky option (i.e. make the safe option more tempting), or decrease the value 50 if they chose the safe option (i.e. make the safe option less tempting). A cool aspect of the ontrialend attribute is that we can create multiple version of this attribute in a single trial element, all of which will be evaluated. The second useful aspect for us, is that each of these ontrialbegin attributes can also accept a conditional statment, or if statement (help is under conditional statement), as an expression. An if statement has 2 parts a logical expression, that is first evaluated, then a secondary expression which is only performed if the logical expression is true. for instance: [if (trial.risk.response == 30) values.current = values.current-50], would check whether the response given on the trial is 30, and if it is would then update the current value. Remember that the A key has a response value of 30 and the L key a value of 38.

Try implementing conditional updating values.current.

<trial risk>
  /ontrialend = [if(trial.risk.response == 30) values.current = values.current-50] #decreases the safe value if they choose safe/left
/ontrialend = [if(trial.risk.response == 38) values.current = values.current+50] #increases the safe value if they choose risky/right
 /stimulustimes = [0=safe; 0=risky]
 /validresponse = ("a", "l")
/pretrialpause = 500
</trial>

3. The titration

Now we have all the skills to implement our titration. Reread the description of the titration from the paper. Think about how you could try and implement it with what you have just learnt. The first decision to consider is what information do we need to track from trial to trial? For each piece of information you need to track you will need to create a custom value. How many will you need?

Three. In the description of the titration you start off with two boundary values, an upper boundary, and a lower boundary. On each trial you then give a value that is the mid-point of these two boundaries, and at the end of the trial replace either the upper or lower boundary with the midpoint, depending upon the participants choice (i.e. the conditional branching). So to do this we will need to update the two boundaries after each trial, and the midpoint as well.

a. Create your variables

First create the 3 variables that you want to track (i.e. create them as attributes of the values element). Let call the upper, lower and current (for the midpoint). What should be the starting values for these 3 custom values?

<values>
/current=1500
/upper=3000
/lower=0
</values>

b. Update the boundaries

In your current code at the end of each trial you update current.values based on the response the participant made. In the titration we also need to update values based on the response the participant made. In particular we need to reset either the upper bound to match the current value, or the lower bound to match the current value, depending upon which choice they made. Updating the boundary itself is easy, you just need to replace the stored value of values.upper or values.lower to values.current using an expression (e.g. values.upper = values.current). Try updating your code. When should you replace the upper bound and when the lower.

<trial risk>
 /stimulustimes = [0=safe; 0=risky]
/ ontrialend=[if (trial.risk.response == 30) values.upper = values.current]
/ ontrialend=[if (trial.risk.response == 38) values.lower = values.current]
/ skip = [values.upper-values.lower<25]
 /validresponse = ("a", "l")
/pretrialpause = 500
</trial>

The upper bound should be replaced whenever they choose the safer option. The upper bound is the minimum amount of money they need to still prefer the safer option, so when they choose the safer option we should lower the upper bound.

The lower bound should, therefore, be replaced whenever they choose the risky option. The lower bound is the maximum amount of money they could be offered for the safe option and still prefer the risky option, so if they choose the risky option we should adjust the maximum accordingly.

c. Update the current value

Now that our bounds are updating, we need to define how the midpoint is calculated each trial. This is fairly easy, the midpoint of 2 values is simply values.current = (values.upper + values.lower)/2. However we don’t really want to present participants with fractions of a dollar. So instead we will want to round this value. Inquisit has a variety of pre-built functions for just this stituation, including a round function. Have a look at the functions helpfile.

Once you’ve worked out how to calculate the new midpoint, you can update values.current during the trial, just like you did in Exercise 2c, just replace the expression with the new one. Can you think of any potential problems with doing this?

At the end of the trial you are updating either the values.upper or values.lower bound based on the values.current, and also updating the values.current based on the both these bounds. For this to work you want Inquisit to first update the bounds, then update values.current. If it instead does it the other way around then you will get nonsense results. So you can either read some documentation/google and see if you can work out how Inquisit handles multiple calls to `ontrialbegin` (and test it out). Or we can avoid the issue completely, by not doing them all after the trial.
i. Order consideration.

We don’t really need to calculate the midpoint at the end of the trial. Instead we could easily do it at the beginning of the next trial. This way we have a clear record of what the boundaries were at the end of each trial, and a record of what the midpoint is that is displayed each trial. Use ontrialbegin instead of ontrialend to update values.current.

<trial risk>
/ontrialbegin = [values.current = round((values.upper+values.lower)/2)] 
 /stimulustimes = [0=safe; 0=risky]
/ ontrialend=[if (trial.risk.response == 30) values.upper = values.current]
/ ontrialend=[if (trial.risk.response == 38) values.lower = values.current]
/ skip = [values.upper-values.lower<25]
 /validresponse = ("a", "l")
/pretrialpause = 500
</trial>

d. Ending the titration

Currently the Experiment runs for 15 trials. In the paper the titration procedure terminates once the distance between the upper and lower bounds is less than $25. This feature is fairly easy to implement.

If you look at the help page for the trial element you will see that there is an attribute called skip. Unsurprisingly this attribute lets you specify situations in which this trial should be skipped. The way to specify when the trial should be skipped is by providing a logical expression to skip. When this expression is true the trail will be skipped entirely. When it is false it will be run as normal.

Try setting the skip attribute so that the process terminates when the difference between the upper and lower bound is less than 25.

<trial risk>
/ontrialbegin = [values.current = round((values.upper+values.lower)/2)] 
 /stimulustimes = [0=safe; 0=risky]
/ ontrialend=[if (trial.risk.response == 30) values.upper = values.current]
/ ontrialend=[if (trial.risk.response == 38) values.lower = values.current]
/ skip = [values.upper-values.lower<25]
 /validresponse = ("a", "l")
/pretrialpause = 500
</trial>

Further Exercises

Good work. There is only one (kind of) further exercise this week. Instead I want you to go back to last weeks WPA and instead complete the exercises there on adding instruction pages (or in WPA3). If you have already done that, then there is one more task.

4. Look at the data file

Have a look at the datafile generated by Inquisit. Does it give you any information about the values of values.current, values.upper or values.lower?

No. Inquisit has a set of default information it records in the data file. This does not include information on user defined variables. You can work them out based on the stimuli that were shown, but this requires some extra processing.

a. Change the datafile

Fortunately Inquisit has an element called data which allows you to specify what data should be recorded. In particular it has an attribute called columns where you can list the names of variables you want to record (as columns). Some of the data names are fairly intuitive. For instance if you specify tat latency should be recorded then you will have a column of response latencies, similarly response will give you a column of response values. For other, like custom values, you can just specify their names as you have elsewhere in you code. Change you datafile so that it only records the custom values. To view the new format properly you’ll have to delete the old data file, or it will try to use the same column structure (in a weird/useless way).

<data>
/ columns = [values.current values.lower values.upper]
</data>

Here is an example of some columns I normally record

<data>
/ columns = [date time subject group build blockcode blocknum trialcode
trialnum pretrialpause response correct latency stimulusonset
stimulusnumber stimulusitem stimulusonset
stimulusnumber stimulusitem values.current values.lower values.upper]
</data>
Good work, you did everything. Email me your script for the week and then you can leave. If this was really easy let me know so that I can adjust the content for next week.