####################### PVTdemoAGEPIQ.py Description #########################
# PVTdemoAGEPIQ.py 10-09-06 replicating Psychomotor Vigilance Task (PVT) model
# by Gunzelmann, G.Gluck, K., Van Dongen, H., & Dinges, D.F. in press (2006)
# Decreased Arousal as a Result of Sleep Deprivation: The Unraveling of 
# Cognitive Control (chapter in Modeling Integrated Cognitive Systems)
# reaction time model for letter X response X with trial interval 2-10 sec
# experimental data collected 10 minutes every 2 hours for 88 hours
# (missing latency feedback to the display)
# simulating total sleep deprivation (TSD) for 0 1 2 3 days
# PythonActor code by Terry Stewart and Bruce Landon
#note: description from Loh,Laland,Dorrian,Roach and Dawson (2004) describing
#their replication of Dinges and Powell (1985) which is not easily avialable.
#PVT ran for a period of 10min. Subjects were required to respond to a visual
#stimulus presented at a variable interval (2000-10000ms) by pressing a button
#with the thumb on the dominant hand. The visual stimulus was a four-digit
#LED counter turning on and incrementing from 0 to 60s at 1ms.  Button press
#stopped the LED incrementing allowing the subject 1s to read their reaction 
#time before the counter restarted.  If a response was not made within 60s
#the clock was reset and the counter restarted. If a response was made prior
#to the presentaiton of the stimulus, a "False Start" message was displayed
#If the button was not released after 3s a reminder message ("Release Button")
#was displayed.(there is now a Palm verison of the PVT Saxena & George (2005)
#
# J Gerontol. 1994 Jul;49(4):P179-89.
# Age differences and changes in reaction time: the Baltimore Longitudinal
# Study of Aging by Fozard JL, Vercryssen M, Reynolds SL, Hancock PA, Quilter
# RE. National Institutes of Health, National Institute on Aging, Nathan W.
# Shock Laboratories of the Gerontology Research Center, Baltimore.
# This study analyzed auditory reaction time (RT) data from 1,265
# community-dwelling volunteers (833 males and 432 females) who ranged in age
# from 17 to 96. Cross-sectional analyses revealed slowing of simple (SRT)
# and relatively greater slowing of disjunctive (DRT; aka "go-no-go") reaction
# time across decades for both males and females. Repeated testing within
# participants (longitudinal analyses) over eight years showed consistent
# slowing and increased variability with age. Males were faster than females
# cross age groups, RT tasks, and visits. Beginning at about age 20, RTs
# increased at a rate of approximately 0.5 msec/yr for SRT and 1.6 msec/yr
# for DRT. Errors also increased, making unlikely a tradeoff of accuracy
# for faster responses. The findings are consistent with the hypotheses that
# slowing of behavior is: (a) a continuous process over the adult life span;
# (b) characterized by age-associated increases in within-participant
# variability; (c) a direct function of task complexity and, presumably, the
# degree of mediation by higher regions in the central nervous system; and
# (d) greater in women than men.
#
# A unifieded computational theory for
# answering 20 questions (and more) about cognitive ageing
# David E. Meyer, Jennifer M. Glass, Shane T. Mueller,
# Travis L. Seymour, and David E. Kieras University of Michigan, USA
# EUROPEAN JOURNAL OF COGNITIVE PSYCHOLOGY, 2001, 13 (1/2), 123–164
# Quoted from p. 130 Cognitive-processor parameters.
# The rate of processing in EPIC is constrained
# by the cognitive processor’s mean cycle time. For young adults,
# we assume that this parameter equals 50 ms. Our assumption is supported
# by both behavioural and psychophysiological data (e.g., KristoVerson,
# 1967). It conforms well with the mean period between zero crossings in
# the brain’s alpha rhythm, which equals about 50 ms for young adults and
# correlates positively with their mean simple RTs (Callaway & Yeager,
# 1960; Surwillo, 1963; WoodruV, 1975). It appears that alpha-rhythm zero
# crossings perhaps correspond to the onsets of cognitive-processor cycles.
# In contrast, for older adults around 70 years of age, the mean zero
# crossing period of the alpha rhythm is about 10–15% longer than for
# young adults (Marsh & Thompson, 1977; Obrist & Busse, 1965; Surwillo,
# 1963), and older adults’ mean simple RTs are also about 10–15% longer
# (Cerella, 1985; Somberg & Salthouse, 1982). These results lead us to
# make the following strong but at least somewhat plausible assumption: In
# EPIC models for older adults (circa 70 years of age), the mean cognitive
# processor cycle time should be set to 56.5 ms, about 13% longer than the
# mean cycle time in models for young adults (circa 20 years of age). This
# assumption is consistent with Kail and Salthouse (1994), who claimed
# that ageing affects ‘‘a fundamental component of the architecture of
# human cognition’’ which functions like the CPU clock of a microcomputer
# whose ‘‘clock speed’’ determines the rate of information processing.
# Our assumption also agrees with Madden (1992), who claimed that in
# visual word-recognition tasks, mean RTs increase by roughly 4–10 ms per
# decade of age. Under EPIC, such tasks usually require about 3–8 cognitive-
# processor cycles to be completed. Moreover, ageing would involve
# roughly a 1.3 ms increase per decade in the mean cycle time. The latter
# factor, multiplied by 3–8 cycles, yields a range close to Madden’s 4–10ms
# per decade.  ... Furthermore, another key parameter that presumably
# mediates RTs and age-related slowing under EPIC is the number of cycles
# taken to complete a task, which depends on what production rules are used
# for task performance (Kieras & Meyer, 2000). Insofar as older adults use
# less effcient rules and so take more cognitive-processor cycles than young
# adults, age-related RT diVerences may be larger than if only the mean
# 130 MEYER ET AL.
# 
# Alpha EEG predicts visual reaction time.
# Jin, Yi, Department of Psychiatry and Human Behavior, University of
# California Irvine College of Medicine, Irvine, CA, US, yjin@uci.edu 
# O'Halloran, James P.,Plon,Lawrence,Sandman, Curt A.,Potkin, Steven G.,
# International Journal of Neuroscience, Vol 116(9), 2006. pp. 1035-1044.
# Abstract:  Studies have suggested that consciousness is encoded
# discretely in time and synchronously in space of the brain. The present
# study was to model the alpha EEG as a brain clock to carry out the
# functions and to test whether the quality and rate of the oscillation
# could predict behavioral timing. Results showed that the alpha peak
# frequency was correlated with the conflict reaction time, and the
# selectivity was associated with the simple reaction time. These findings
# are consistent with previous reports and support the hypothesis that
# alpha EEG represents excitability cycles and may serves as a brain
# clock for spatial synchronization.
# 
######################## Define task setup ##################################
repetitions=100 # the number of stimuli to show in simulation run
goal_threshold = 1.98 #enable ccm simulation 1.98 1.80 1.66 1.58 TSD 0 1 2 3 
utility_threshold = 1.84 #enable simulation  1.84 1.78 1.70 1.64 TSD 0 1 2 3
cycle_noise = 0.0125 # variability in fundamental cycle time of 50ms
adjust_utility_threshold = 0.035 # reduce utility threshold after idle cycle
######################## Define personal parameters #########################
Agefix = 0.0 # ((age -20)*0.00013)lengthen fundamental cycle for aging
# (IQz * -0.31 = RTz) assume like IQ decline with aging -1 sd = 30yr older
# from Salthouse (1992) Anderson text 2004 p.437 (PIQ85 at 20=50 with PIQ100)
PIQfix = -0.0 # PIQ100=0 PIQ085=+0.0039 PIQ115=-0.0039, PIQ130=-0.0078
####################### Editable parameters above ###########################
from ccm.env.objects import *#http://www.carleton.ca/ics/ccmlab/ccmsuite.html
import random
import math

class MatchEnvironment(ObjectEnvironment):
    def start(self):     # define internal variables
        self.count=0
        self.falseAlarm=0
        self.lapse=0
        self.sleepAttack=0
        self.letter=Object(isa='letter',x=0.5,y=0.5,value=None) # initialize
        self.numrt=0     # for calculating okrtaverage
        self.sumrt=0     # for calculating okrtaverage
        self.rtsumX=0    # for calculating self.okrtsd
        self.rtsumXsqr=0 # for calculating self.okrtsd
        self.rtnum=0     # for calculating self.okrtsd
        yield random.uniform(2,10) # random wait 2-10 seconds
        self.letter.value='X'
        self.target=self.letter.value
        self.cueTime=self.now()    # get the current time

    @ccm.parallelize    
    def press(self,key):
        rt=self.now()-self.cueTime  # calculate reaction time
        log.rt=rt    # record the reaction time to the log
        if rt<0.150 or self.letter.value==None: # response before stimulus
            self.falseAlarm+=1
        elif rt>30: 
            self.sleepAttack+=1
        elif rt>0.5:
            self.lapse+=1
        else:
            self.numrt+=1    # ok rt between >0.15 (falseAlarm) <0.5 (lapse)
            self.sumrt+=rt
            self.rtsumX+=rt
            self.rtsumXsqr+=(rt * rt)
            self.rtnum+=1
        self.letter.value=None
        self.count+=1
        if self.count==repetitions: # report results in log
            # convert to proportions
            scale=1.0/repetitions
            log.falseAlarm=self.falseAlarm*scale
            log.lapse=self.lapse*scale
            log.sleepAttack=self.sleepAttack*scale
            log.oktraverage=self.sumrt/self.numrt # over 150ms under 500ms
            log.self.okrtsd= math.sqrt((self.rtnum * self.rtsumXsqr
                                        -self.rtsumX * self.rtsumX)/
                                       (self.rtnum * (self.rtnum-1)))
            print 'Age20fix=' + str(Agefix)  # via production cycle time
            print 'PIQ100fix=' + str(PIQfix)  # via production cycle time
            print 'alphaEEG=' + str((0.05 + Agefix + PIQfix)*200) #normal
            self.stop()
        else:
            self.model.pgc.goal=goal_threshold # reset goal at start of trial
            yield random.uniform(2,10) # random inter-trial interval 2-10s
            self.letter.value='X' # the cue stimuls (for rt counter digits)
            self.target=self.letter.value
            self.cueTime=self.now() # start trial reaction timer
            
from ccm.lib.actr import * # http://www.carleton.ca/ics/ccmlab/ccmsuite.html
class PMpgcGoalReducer(PMpgc): # enables falling asleep or micro sleeps
   def below_threshold(self):
       if env.letter.value==None: return  # don't reduce G if no stimuli
       self.goal-=adjust_utility_threshold # reduce threshold after idle cycle
       if self.goal < 0 :    # self.goal is G which is motivational goal value
            self.goal=0.0001 #keep G positive even during sleep attack

################# Define the ACT-R Model Components ##########################   
class MyModel: # minimal model (with no memory,mood,feeling,pain or alertness) 
  goal=Buffer()
  visual=Buffer()
  env=DirectEnvironment()
  vision=SOSVision(visual,delay=0.085, delay_sd=0.01)
  procedural=Procedural(threshold=utility_threshold,
                        delay=(0.05 + Agefix + PIQfix),delay_sd=cycle_noise)
  pgc=PMpgcGoalReducer(procedural,goal=goal_threshold) #sleepless adjust G
  pnoise=PMNoise(procedural,0.25) # add in the noise part of the utility 
  
################# Define ACT-R Model Production Memories #####################
  def attend(goal='attend',vision='busy:False'): # looking for stimulus X
    vision.request('isa:letter')
    goal.set('attend')
    
  def respond(goal='attend',visual='isa:letter value:?letter'): #see X
    env.press(letter) # model presses key X to stop the reaction time clock
    visual.clear()
    
  def just_click(goal='attend',vision='busy:False'): # enables false alarms
    env.press('X') # press X before stimulus appears due to model noise
    visual.clear()

####################### Define Environment setup ############################
log=ccm.log()            # enable log to print out results
env=MatchEnvironment()   # don't automatically try to log things
env.model=ACTR(MyModel,logging=False) #turn off logging internal cycle states
env.model.goal.set('attend') # set starting goal buffer
# needed to set the P value for 'just_click' to zero 
env.model.pgc.set('just_click',successes=0,failures=1,lock=True)
# turn on visual display or just run env.run()
#import ccm.display
#ccm.display.Display(env)
#env.run(rate=1)  # to run at real-time    
env.run() # run simulation with model

