Difference between revisions of "Trial Structure"
Revision as of 16:16, 12 June 2007
Trial Structure employed by the Real-Time Linux State Machine
Most often, we organize behavioral experiments around successive “trials.” But the state machine, as described so far, has no concept of “trials.” This is what we introduce now. Typically what happens in between trials is that the state machine diagram changes: for example, in one trial, poking in the Left might be the correct response, whereas in the next trial, poking in the Right might be the correct response. The Governing Machine has to tell the RTLSM to change the state diagram to reflect that. Now, changing the state diagram is a tricky affair: we want to be careful about changing a program in the middle of running it! In addition, we want changes between trials to be fast --instantaneous if possible,-- so the behavior box is always responsive to the subject. But anytime the RTLSM is waiting for the Governing Machine to tell it something, real-time guarantees are gone. How we address these issues, and how we set things up so transitions between trials are almost guaranteed to be instantaneous, is described below.
There are three types of special states that define the trial structure.
State 0. The first special state is state_0, which is the very first state in any State Machine. Going through this state is taken as delimiting the start of a trial. By the same token, going through state_0 indicates that the previous trial has ended. In other words, state_0 indicates a boundary between trials.
prepare_next_trial set of states. As the RTLSM is going through its states and transitions, the Governing Machine periodically polls the RTLSM to ask what's been happening. If the Governing Machine finds that the RTLSM has entered one of the states belonging to the "prepare_next_trial" set of states, then the Governing Machine takes that as a cue to prepare, and send to the RTLSM, the definition of the state machine for the next trial. That definition won't take effect until the RTLSM jumps to state_0 again. Any time a state machine definition is sent, the user also determines which states in that definition belong to the "prepare_next_trial" set.
(A typical setup might be one where each trial ends in either a reward or a timeout; each of those might last a second or longer. The reward state and the timeout state might together compose the "prepare_next_trial" set. This way, as soon as either of those states is entered (and the outcome of the trial is therefore known), the Governing Machine starts working on preparing the next trial, without having to wait for the reward or timeout state to end. Thus we maximize the time the Governing Machine has available to make the next trial ready before the current trial ends.)
While the Governing Machine is preparing the definition of next trial's state machine, the RTLSM of course keeps running. Immediately after the Governing Machine has sent the definition for the next trial's state machine, it sends the RTLSM a signal that means "next trial is ready."
check_next_trial_ready. If the RTLSM hits the "check_next_trial_ready" state, and the Governing Machine has sent the "next_trial_is_ready" signal, then the RTLSM jumps to state_0 and at that point the new state machine definition takes effect. Typically you will want the Governing Machine to work fast, and send the next trial's state machine definition before the RTLSM reaches "check_next_trial_ready;" but it needn't always be so. The RTLSM could go several times through "check_next_trial_ready" before finding the "next_trial_ready" flag set. It is important to keep this possibility in mind, because the Governing Machine doesn't run real-time: thus, you can set the Governing Machine up so it is extremely likely that it will work fast and send the next trial's definition in time. But you can't 100% guarantee that it will.
With this setup, the RTLSM is always active and responding to the subject, the switching of state machines in between trials is done cleanly and at a well-defined point, and you can easily write state machines where it is almost certain that on every trial the jump to state_0 will happen the first time the RTLSM reaches "check_nect_trial_ready."
In the historical default configuration (known as RPbox.m), "prepare_next_trial" and "check_next_trial_ready" were both a single state, and the same one at that! That historical default state is state number 35. With the benefit of hindsight (isn't everything easier in hindsight?) that wasn't so great, because "prepare_next_trial" and "check_next_trial_ready" are really two very different kinds of situations and require different treatment. In newer setups (for example, in dispatcher), "prepare_next_trial" and "check_next_trial_ready" can be different states, and "prepare_next_trial" may be composed of several different states.
[As a note for people interested in the innards, we point out that "state_0" and "check_next_trial_ready" are defined in, and are part of, the RTLSM code. But the "prepare_next_trial_states" are defined, and used, entirely within the Governing Machine. The RTLSM need never know which states are part of "prepare_next_trial" and which aren't.]