StateMachineAssembler-parsing

From Bcontrol
Revision as of 07:37, 28 June 2007 by CarlosBrody (talk | contribs) (The parsed structure)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Through its method "disassemble," the StateMachineAssembler can take the raw output from the RTLSM (which is a string of numbers) and turn it into intelligible (i.e., English-readable) state names, poke and event types, and time stamps.


You can ask for the parsing results in one of two modes:

  1. As a parsed structure, in which there are entries for each state name and for each event type.
  2. As a blow-by-blow list of named events.


The parsed structure[edit]

This "parsed structure" format is the format that Dispatcher will automatically provide for you.

Suppose that your state machine definition has states "alpha" and "beta" (as well as "state_0," which all state machines have). And suppose that you have binary input lines coming from the behavior box, named "C" and "Lever." (The default binary input lines are "C", "L", and "R", corresponding to Center, Left, and Right nose pokes, but these can be changed.)

You can then take a series of events obtained from the RTLSM, run them through disassemble, and get a parsed structure (let's call it pstruct) that will have elements

  pstruct.states.alpha
  pstruct.states.beta
  pstruct.states.state_0
  pstruct.states.starting_state
  pstruct.states.ending_state
 

Each of the entries in pstruct.states.(statename) will be an n-by-2 numeric matrix. The number of rows, n, will be the number of different times the state machine was in a state with the corresponding name. If you never entered the state, then n=0 and this will be an empty matrix. The first column in the matrix will have the time at which the state was entered; and the second column has the time at which the state was exited. Thus, for example,

  pstruct.states.alpha = [1.32 1.55 ; 2.2 3]

means that you entered state alpha at t=1.32; exited it at t=1.55; entered it again at t=2.2; and exited at t=3. A NaN in any of these positions indicates that, given the events passed to it, disassemble cannot tell when the corresponding time stamp occurred. Thus, for example,

 pstruct.states.alpha = [1.4 NaN]

means that you entered state alpha at t=1.4; but up until the last event given to the disassembler, you hadn't exited state alpha, so the disassembler can't say what the exit time is. When given the precise set of events that form a completed trial (i.e., from state 0 to when the the state machine next jumps to state 0 for the next trial and switches state machine definitions to the definition for the next trial -- see Trial Structure), then pstruct.states.state_0 will be guaranteed to have the form [NaN trial_start_time ; trial_end_time NaN]. That is, the trial started when you left state_0 to go into your state machine definition; and ended when you jumped to state_0 again.

The elements pstruct.states.starting_state and pstruct.states.ending_state will both contain strings. The first will contain the name of the state in which the RTLSM was in at the beginning of the events that the disassembler is parsing; and the second will contain the name of the state in which the RTLSM was at the end of the events being parsed.


In addition to information about events, the pstruct will also contain the following elements with information about input lines from the behavior box:

  pstruct.pokes.C
  pstruct.pokes.Lever
  pstruct.pokes.starting_state.C
  pstruct.pokes.starting_state.Lever
  pstruct.pokes.ending_state.C
  pstruct.pokes.ending_state.Lever

The elements pstruct.pokes.(line_name) will be of the same format as pstruct.states.(statename); that is, they will be n-by-2 numeric matrices, which each row containing the starting and ending times of each input line being high. Thus, for example,

  pstruct.pokes.C = [4.32 6.6 ; 6.61 NaN]

means that the animal was IN the Center poke from time t=4.32 to t=6.6; then pulled out; then poked in again at t=6.61; and by the time of the last event being parsed, hadn't yet pulled out.

A key difference between pokes and states is that, by definition, you can only be in one state at a time-- but it is possible for more than one input line to be high at any one time. For example, the animal could have its nose IN the Center poke while at the same time pressing the Lever down. For this reason, the starting_state and the ending_state of the pokes cannot be a single string, as it was for the states. Therefore, for each type of input line ("C" and "Lever" in our example here), there is one element in pstruct.pokes.starting_state and one element in pstruct.pokes.ending_state. This element will contain one of three things: the string 'in', meaning the input line was high; the string 'out', meaning the input line was low; or an empty matrix, meaning the disassembler can't tell given the information it was given.

Thus, for example, suppose there is a string of events in which the animal never changes what it is doing to the Lever (it either keeps it pressed all the time or keeps it unpressed all the time); and within that string of events, the animal pokes once into the Center poke and then keeps its nose there. Then you would get

  pstruct.pokes.starting_state.C     = 'out'
  pstruct.pokes.starting_state.Lever = []
  pstruct.pokes.ending_state.C       = 'in'
  pstruct.pokes.starting_state.Lever = []

Thanks to the transition from out to in, there is information that allows the disassembler to tell that that C line was initially low and ended high; but it doesn't know about the state of the Lever.

Of course, as you follow longer and longer sequences of events, you can trace the transitions of all input lines from the beginning and and therefore know, at any point, whether they are high or low. There is a StateMachineAssembler method, stitch_chunks.m, that helps you take two consecutive stretches of events and combine their disassembled events together in a way that follows the trace of the input lines, keeping track of whether they are high or low. However, if you are using Dispatcher, all that is done automatically for you and you don't have to worry about it: Dispatcher makes sure to keep proper track of what the input lines are doing, and so the starting_state and the ending_state of the pokes will have appropriate 'in' or 'out' information always.