;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; This file contains the ACT-R model of the
;;; Johnston Experiment presented in Chapter 5
;;;
;;; ACT-R version 4 required
;;;
;;; A WWW interface and a command line interface
;;; are provided.
;;; To run the command line version, call
;;; (doit-johnston n)
;;; replacing n with the number of runs to
;;; simulate.
;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; This section contains the LISP functions to simulate
;;; the experiment, implement the interface, collect the
;;; data, and display the results
;;;
;;; The ACT-R Model starts further down
;;;
;;; Global variables
(defvar *noise*)
(defvar *stop-it*)
(defvar *sf*)
(defvar *ff*)
(defvar *ft*)
(defvar *st*)
(defvar *items*)
(defvar *data*)
(defvar *word*)
(defvar *read-time*)
(defvar *intercept*)
(defvar *test-flag*)
(defvar *johnson*)
(defvar *word-number*)
(defvar *threshold*)
(defvar *factor*)
(defvar *v*)
(defvar *runs*)
(defvar *penalty*)
(defvar *respond*)
(defvar *encode*)
(defvar *rehearse*)
(setf *respond* .5)
(setf *encode* .2)
(setf *rehearse* .5)
(setf *intercept* .55)
(setf *noise* .55)
(setf *threshold* .9)
(setf *factor* 1.3)
(setf *word-number* 1.0)
(setf *v* nil)
(setf *penalty* 15)
(defparameter *johnston-contexts* '(l1 l2 l3 l4 l5 l0))
(defparameter *johnston-data* (make-array '(2 4 2) :initial-contents
'(((.73 .27) (.72 .28) (.39 .61) (.35 .65))
((1.313 1.521) (1.531 1.737)(1.455 1.67)(1.789 1.918)))))
(defparameter *johnston-results* (make-array '(2 4 2) :initial-contents
'(((73 27) (72 28) (39 61) (35 65))
((95.849 41.067) (110.232 48.636)(56.745 101.87)(62.615 124.67)))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; This section contains the interface for the WWW using the
;;; ACT-R on the Web application by Elmar Schwarz
(defvar *WWW-interface*)
(setf *WWW-interface*
'((:heading "Johnston simulation" 2)
(:table)
(:table)
"Noise (s): " (:string :sy *noise* .55) (:new-row)
"Threshold: " (:string :sy *threshold* .9) (:new-row)
"Scale factor (F): " (:string :sy *factor* 1.3) (:new-row)
"Mismatch penalty:" (:string :sy *penalty* 1.5) (:new-row)
"Response time (sec.): " (:string :sy *respond* .5) (:new-row)
"Encoding time (sec.): " (:string :sy *encode* .2) (:new-row)
"Rehearsal time (sec.): " (:string :sy *rehearse* .5) (:new-row)
"Encodeing time of fast test (sec.): " (:string :sy *intercept* .55) (:new-row)
"Number of runs (1-20): " (:string :sy *runs* 1)
(:table-end)
(:hidden :sy *word-number* 1.0)
(:table)
(:checkbox "Trace" :sy *v* nil)
(:table-end)
(:table-end)
(:new-para)
(:button "Show Experiment Results" "(progn
(format *standard-output* \"~%~%Experimental data:~%\")
(output-johnston *johnston-results* (/ 1 .44)))")
(:new-para)
(:button "Run model" "(progn
(when (numberp *penalty*)
(setf *penalty* (* 10 *penalty*)))
(if (and (numberp *noise*) (numberp *threshold*)
(numberp *factor*) (numberp *penalty*)
(numberp *respond*) (numberp *encode*)
(numberp *rehearse*) (numberp *intercept*)
(numberp *runs*))
(doit-johnston (min 20 (max 1 *runs*)))
(format *standard-output* \"All parameters must be numbers.~%\")))" )
(:reset "Default values")
(:button "Production Rules" "(let ((prods (no-output (pp))))
(dolist (x prods) (pp-fct (list x))
(spp-fct (list x))
(format *standard-output* \"~%\")))")
(:button "Chunk types" "(chunk-type)")
(:button "Chunks" "(dm)")
(:new-para)
"TIME and SIZE:"
(:new-para)
"- It usually takes about 1 minute for 1 run of the model"
(:new-line)
"- The trace of 1 run is approximatly 125k (80 pages) in size"
(:new-para)))
;;; make-up-items-johnston takes 2 parameters
;;; a count of the number of words (m) and the
;;; number of letters to use to form the words (n)
;;; and then generates a set of letter chunks for each of
;;; the 4 positions in the word, and creates chunks
;;; for m random words, setting the baselevels
;;; of the words very low, *word-number* times out
;;; of the last 1000 (*word-number* is set to 1)
;;; and returns the list of word chunks
(defun make-up-items-johnston (m n)
(let* ((first (make-up-letters-johnston "FIRST" n))
(first (append first (nthcdr (floor n 2) first) (nthcdr (* 3 (floor n 4)) first)
(nthcdr (* 7 (floor n 8)) first)(nthcdr (* 7 (floor n 8)) first)))
(second (make-up-letters-johnston "SECOND" n))
(second (append second (nthcdr (floor n 2) second)(nthcdr (* 3 (floor n 4)) second)
(nthcdr (* 7 (floor n 8)) second)(nthcdr (* 7 (floor n 8)) second)))
(third (make-up-letters-johnston "THIRD" n))
(third (append third (nthcdr (floor n 2) third)(nthcdr (* 3 (floor n 4)) third)
(nthcdr (* 7 (floor n 8)) third)(nthcdr (* 7 (floor n 8)) third)))
(fourth (make-up-letters-johnston "FOURTH" n))
(fourth (append fourth (nthcdr (floor n 2) fourth)(nthcdr (* 3 (floor n 4)) fourth)
(nthcdr (* 7 (floor n 8)) fourth)(nthcdr (* 7 (floor n 8)) fourth))))
(do ((count 0 (1+ count))
(result nil (cons (make-word-johnston (randmem-johnston first) (randmem-johnston second)
(randmem-johnston third) (randmem-johnston fourth))
result)))
((equal count m) (reset-ia)
(setlevel-johnston *word-number* (no-output (do-up-ias-johnston (add-dm-fct result))) 1000)))))
;;; setlevel-johnston takes 3 parameters,
;;; the number of occurences (count), a list of
;;; chunks (lis), and the time for those occurences (time)
;;; it then sets the base level of the chunks in the list
;;; to have occured count times since being created at time
;;; time
(defun setlevel-johnston ( count lis time)
(do ((temp lis (cdr temp)))
((null temp) lis)
(eval `(set-base-levels (,(car temp) ,count ,(- time))))))
;;; do-up-ias-johnston takes one parameter
;;; a list of word chunks
;;; for each word in the list the ia
;;; between the letters and the word is increased
;;; by 2
(defun do-up-ias-johnston (lis)
(do ((temp lis (cdr temp)))
((null temp) lis)
(set-ia-fct (list (list (chunk-slot-value-fct (car temp) 'first) (car temp)
(+ 2.0 (ia-fct (chunk-slot-value-fct (car temp) 'first) (car temp))))
(list (chunk-slot-value-fct (car temp) 'second) (car temp)
(+ 2.0 (ia-fct (chunk-slot-value-fct (car temp) 'second) (car temp))))
(list (chunk-slot-value-fct (car temp) 'third) (car temp)
(+ 2.0 (ia-fct (chunk-slot-value-fct (car temp) 'third) (car temp))))
(list (chunk-slot-value-fct (car temp) 'fourth) (car temp)
(+ 2.0 (ia-fct (chunk-slot-value-fct (car temp) 'fourth) (car temp))))))))
;;; randmem-johnston takes one parameter
;;; a list, and returns a random item from
;;; the list
(defun randmem-johnston (lis)
(nth (random (length lis)) lis))
;;; make-up-letters-johnston takes two parameters
;;; a string identifier, and the number of items
;;; it then generates that number of letter chunks
;;; with the name of the chunks starting with
;;; the identifier
(defun make-up-letters-johnston (index n)
(do ((count 0 (1+ count))
(result nil (cons `(,(gentemp index) isa letter) result)))
((equal count n) (add-dm-fct result))))
;;; make-word-johnston takes four parameters,
;;; all letter chunks, and returns the act-r
;;; definition of the word composed of those
;;; four letters, that can be passed to
;;; add-dm
(defun make-word-johnston (a b c d)
`(,(gentemp "W") isa word first ,a second ,b
third ,c fourth ,d))
;;; setup-materials-johnston takes 2 parameters
;;; the number of words (m) and the number
;;; of letters used to make those words (n)
;;; and calls the functions to generate the
;;; letters and words, and the memory tokens
;;; for previous instances of the words,
;;; and then sets the base levels for the
;;; previous memory tokens, and returns the
;;; list of word chunks
(defun setup-materials-johnston (m n)
(let ((words (make-up-items-johnston m n)))
(do ((temp words (cdr temp))
(items nil (append (setup-johnston (car temp) 3) items)))
((null temp)
(setlevel-johnston 3 (add-dm-fct items :reset-ia nil) 400)
words))))
;;; setup johnsto takes 2 parameters,
;;; a word chunk and a count of previous
;;; occurences. it then creates a list of
;;; chunk definitions for that many different
;;; previous occurences of that word,
;;; and returns the list
(defun setup-johnston (word n)
(do ((count 0 (1+ count))
(result nil (cons `(,(gentemp "C") isa memory-token
context ,(nth count *johnston-contexts*)
first ,(chunk-slot-value-fct word 'first)
second ,(chunk-slot-value-fct word 'second)
third ,(chunk-slot-value-fct word 'third)
fourth ,(chunk-slot-value-fct word 'fourth)
name ,word) result)))
((= count n) result)))
;;; doit-johnston takes on parameter,
;;; the number of trials of the experiment to
;;; simulate, it runs that many simulations
;;; of the experiment and then displays the
;;; results
(defun doit-johnston (n)
(setf *data* (make-array '(2 4 2) :initial-element 0))
(do ((count 0 (1+ count)))
((equal count n) nil)
(reset)
(set-params-johnston)
(run-experiment-johnston))
(format *standard-output* "~%~%Parameters for run: (~S ~S ~S ~S ~S ~S ~S ~S ~S)~%~%Simulation data:~%"
*noise* *threshold* *factor* *penalty* *respond* *encode* *rehearse* *intercept* n)
(output-johnston *data* n))
;;; output-johnston takes 2 parameters,
;;; the array containing the data for
;;; runs of the johnston experiment, and the
;;; number of runs represented in the data
;;; and displays the accuracy and response time
;;; averages for the different conditions
(defun output-johnston (stats n)
(format *standard-output* "~%Fast Encoding~%")
(format *standard-output* " Accuracy % RT (sec.)~%")
(format *standard-output* "Hits: ~4,1F% ~5,3F~%" (/ (aref stats 0 0 0) (* .44 n)) (if (zerop (aref stats 0 0 0))
0.0
(/ (aref stats 1 0 0) (aref stats 0 0 0))))
(format *standard-output* "Misses: ~4,1F% ~5,3F~%" (/ (aref stats 0 0 1) (* .44 n)) (if (zerop (aref stats 0 0 1))
0.0
(/ (aref stats 1 0 1) (aref stats 0 0 1))))
(format *standard-output* "FAs: ~4,1F% ~5,3F~%" (/ (aref stats 0 2 0) (* .44 n)) (if (zerop (aref stats 0 2 0))
0.0
(/ (aref stats 1 2 0) (aref stats 0 2 0))))
(format *standard-output* "Rejects: ~4,1F% ~5,3F~%" (/ (aref stats 0 2 1) (* .44 n)) (if (zerop (aref stats 0 2 1))
0.0
(/ (aref stats 1 2 1) (aref stats 0 2 1))))
(format *standard-output* "~%Slow Encoding~%")
(format *standard-output* " Accuracy % RT (sec.)~%")
(format *standard-output* "Hits: ~4,1F% ~5,3F~%" (/ (aref stats 0 1 0) (* .44 n)) (if (zerop (aref stats 0 1 0))
0.0
(/ (aref stats 1 1 0) (aref stats 0 1 0))))
(format *standard-output* "Misses: ~4,1F% ~5,3F~%" (/ (aref stats 0 1 1) (* .44 n)) (if (zerop (aref stats 0 1 1))
0.0
(/ (aref stats 1 1 1) (aref stats 0 1 1))))
(format *standard-output* "FAs: ~4,1F% ~5,3F~%" (/ (aref stats 0 3 0) (* .44 n)) (if (zerop (aref stats 0 3 0))
0.0
(/ (aref stats 1 3 0) (aref stats 0 3 0))))
(format *standard-output* "Rejects: ~4,1F% ~5,3F~%" (/ (aref stats 0 3 1) (* .44 n)) (if (zerop (aref stats 0 3 1))
0.0
(/ (aref stats 1 3 1) (aref stats 0 3 1)))))
;;; run-experiment-johnston takes no parameters
;;; it generates the materials for one run
;;; of the johnston experiment, and then calls
;;; the act-r simulation
(defun run-experiment-johnston ()
(let* ((materials (setup-materials-johnston 206 26))
(pre (nthcdr 202 materials))
(materials (nthcdr 4 (reverse materials)))
(post (nthcdr 198 materials))
(materials (nthcdr 4 (reverse materials)))
(fast-t (nthcdr 154 materials))
(materials (nthcdr 44 (reverse materials)))
(slow-t (nthcdr 110 materials))
(materials (nthcdr 44 (reverse materials)))
(buffer (nthcdr 88 materials))
(materials (nthcdr 22 (reverse materials)))
(fast-f (nthcdr 44 materials))
(slow-f (nthcdr 44 (reverse materials)))
(study (append pre (permut-johnston (append fast-t slow-t)) post))
(test (append (permut-johnston (append pre post buffer))
(permut-johnston (append fast-t slow-t fast-f slow-f)))))
(setsimilarities (l0 l1 .9)(l0 l2 .9)(l0 l3 .9)
(l1 l2 .9)(l1 l3 .9))
(setf *test-flag* nil)
(setf *stop-it* 0)
(setf *ft* fast-t)
(setf *st* slow-t)
(setf *ff* fast-f)
(setf *sf* slow-f)
(add-dm-fct '((goal isa study-words context L0)) :reset-ia nil )
(setf *items* study)
(wmfocus goal)
(run)
(setf *items* test) (setf *test-flag* t)
(add-dm-fct '((newgoal isa recognize-words context L0)) :reset-ia nil)
(wmfocus newgoal)
(run)))
;;; permut-johnston takes one parameter
;;; a list, and returns a randomly
;;; permuted version of that list
(defun permut-johnston (lis)
(do ((result (list (nth (random (length lis)) lis))
(cons (nth (random (length lis)) lis) result)))
((null lis) result)
(setf lis (remove (car result) lis :count 1))
(cond ((null lis) (return result)))))
;;; set-params-johnston is used to set
;;; the parameters of the model before each run
;;; it is needed for the WWW interface
(defun set-params-johnston ()
(sgp-fct (list :bll .5 :ans *noise* :mp *penalty* :era t :ol t
:pm t :rt *threshold* :lf *factor* :v *v*))
(parameters-fct 'rehearse-skip (list :r .5 :effort *rehearse*))
(parameters-fct 'rehearse-token (list :effort *rehearse* :strength 2))
(parameters-fct 'attend (list :effort *encode*))
(parameters-fct 'attend-test (list :effort *intercept*))
(parameters-fct 'accept-word (list :effort *respond*))
(parameters-fct 'reject-a-word-2 (list :r .5 :effort *respond*))
(setf *pop-upon-failure* t))
;;; get-word-johnston takes one parameter,
;;; a goal chunk
;;; it places the letters of the word being presented
;;; into the goal, increments the time until the next
;;; word is to be presented, and if it is currently
;;; being tested on a word from the slow presentation
;;; condition adds .2 seconds to the time of encodeing
(defun get-word-johnston (goal)
(setf *stop-it* (+ 2.0 *stop-it*))
(setf *word* (pop *items*))
(cond ((and *test-flag* (or (member *word* *st*) (member *word* *sf*)))
(actr-time .2)))
(let ((first (chunk-slot-value-fct *word* 'first))
(second (chunk-slot-value-fct *word* 'second))
(third (chunk-slot-value-fct *word* 'third))
(fourth (chunk-slot-value-fct *word* 'fourth)))
(mod-chunk-fct goal `(first ,first second ,second third ,third fourth ,fourth))))
;;; analyze-johnston takes 2 parameters
;;; a word chunk and a response,
;;; it then updates the data array
;;; to record the response and the
;;; time of the response
(defun analyze-johnston (word response)
(let ((i))
(cond ((member word *ft*)(setf i 0))
((member word *st*)(setf i 1))
((member word *ff*)(setf i 2))
((member word *sf*)(setf i 3)))
(cond ((not i) nil)
((eq response 'yes) (setf (aref *data* 0 i 0)
(1+ (aref *data* 0 i 0)))
(setf (aref *data* 1 i 0) (+ (aref *data* 1 i 0) (min 10.0 *read-time*))))
(t (setf (aref *data* 0 i 1)
(1+ (aref *data* 0 i 1)))
(setf (aref *data* 1 i 1) (+ (aref *data* 1 i 1) (min 10.0 *read-time*)))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; This section contains the ACT-R model
;;;
;;;
(clearall)
(sgp-fct (list :bll .5 :ans *noise* :mp *penalty* :era t :ol t
:pm t :rt *threshold* :lf *factor* :v nil))
(chunk-type study-words context)
(chunk-type recognize-words context)
(chunk-type recognize-word context target first second third fourth)
(chunk-type word first second third fourth)
(chunk-type memory-token name context first second third fourth)
(chunk-type letter)
(chunk-type context)
(chunk-type rehearsal-fact count name)
;;; add the contexts to memory
(add-dm (l0 isa context)
(l1 isa context)
(l2 isa context)
(l3 isa context))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; Productions
;;;
(p attend
"
IF the goal is to study words
and there are items left to study
and a new word has been presented
THEN
create a memory token for the
letters of the word being presented in
the current context, and push it as a goal
"
=goal>
isa study-words
context =context
!eval! (>= (actr-time) *stop-it*)
!eval! *items*
==>
=newgoal>
isa memory-token
context =context
!eval! (get-word-johnston =newgoal)
!push! =newgoal
)
(parameters-fct 'attend (list :effort *encode*))
(p create-token
"
IF the goal is a memory token of the
letters of a word
and a word chunk having those letters
can be recalled
THEN mark the word in the goal
and create a reharsal fact chunk for the word
and focus on the rehersal fact
"
=goal>
isa memory-token
first =1
second =2
third =3
fourth =4
=name>
isa word
first =1
second =2
third =3
fourth =4
==>
=goal>
name =name
=token>
isa rehearsal-fact
name =name
!focus-on! =token
)
(p initialize-count
"
IF the goal is a rehearsal fact
THEN set the count of the fact to 0
and pop the goal
"
=goal>
isa rehearsal-fact
==>
=goal>
count 0
!pop!
)
(p recognize-count
"
IF the goal is a rehearsal fact
and a rehearsal fact can be recalled
with the same name
THEN mark the goal with the count from
the recalled fact
and pop the goal
"
=goal>
isa rehearsal-fact
name =name
=mem>
isa rehearsal-fact
name =name
count =count
==>
=goal>
count =count
!pop!
)
(p rehearse-token
"
IF the goal is to study words
and there is still time to study this
word (a new word has not yet been presented)
and a memory-token can be retrieved with
the current context
and the word in that token can be retrieved
and the rehearsal fact for that word has
a count that is less than 3
THEN increment the count in the rehearsal fact
"
=goal>
isa study-words
context =context
!eval! (< (actr-time) *stop-it*)
=memory>
isa memory-token
name =name
context =context
=name>
isa word
=token>
isa rehearsal-fact
count =count
name =name
- count 3
==>
=token>
count (!eval! (1+ =count))
)
(parameters-fct 'rehearse-token (list :effort *rehearse* :strength 2))
(p rehearse-skip
"
IF the goal is to study words
and there is still time to study this
word (a new word has not yet been presented)
THEN do nothing
"
=goal>
isa study-words
context =context
!eval! (< (actr-time) *stop-it*)
==>
)
(parameters-fct 'rehearse-skip (list :r .5 :effort *rehearse*))
(p attend-test
"
IF the goal is to recognize words
and there are words to recognize
THEN create a new goal to recognize
the word in the current context
and store the letters into the new goal
and push the new goal
"
=goal>
isa recognize-words
context =context
!eval! *items*
==>
!eval! (setf *read-time* (actr-time))
=newgoal>
isa recognize-word
context =context
!eval! (get-word-johnston =newgoal)
!push! =newgoal
)
(parameters-fct 'attend-test (list :effort *intercept*))
(p probe-token-say
"
IF the goal is to recognize a word
and a word with the same letters can be recalled
THEN marke that word in the goal
"
=goal>
isa recognize-word
first =1
second =2
third =3
fourth =4
target nil
=name>
isa word
first =1
second =2
third =3
fourth =4
==>
!eval! (setf *read-time* (+ .5 (- (actr-time) *read-time*)))
=goal>
target =name
)
(parameters probe-token-say :effort .5)
(p default-token-say
"
IF the goal is to recognize a word
THEN do nothing
"
=goal>
isa recognize-word
first =1
second =2
third =3
fourth =4
target nil
==>
)
(parameters default-token-say :r .5)
(p accept-word
"
IF the goal is to recognize a word
and there is a memory token of that
word occuring in the current context
THEN respond yes
and pop the goal
"
=goal>
isa recognize-word
target =word
context =context
=token>
isa memory-token
name =word
context =context
==>
!eval! (analyze-johnston *word* 'yes)
!pop!
)
(parameters-fct 'accept-word (list :effort *respond*))
(p reject-a-word-2
"
IF the goal is to recognize a word
THEN respond no
and pop the goal
"
=goal>
isa recognize-word
target =word
context =context
==>
!eval! (analyze-johnston *word* 'no)
!pop!
)
(parameters-fct 'reject-a-word-2 (list :r .5 :effort *respond*))