;============================================================= ; Lisp for recognizing artificial grammar ; based on ACT-R tutorial's Choice model 19-22 March 2009 ; ; by W.G. Kennedy, GMU ; wkennedy@gmu.edu ; ; 21 Dec 2011 ; ; ;=============================================================== ; variables used in running experiments (defvar *response*) ; response to a single sequence (defvar *allresponses*) ; responses to all trials (defvar *items*) ; max number of items in sequences (defvar *trials-left*) ; number of trials (defvar *all-valid-strings*) ; all grammar strings <= 8 long (defvar *trainingset*) ; training strings from the grammar (defvar *validset*) ; valid strings available for testing purposes (defvar *foils*) ; foils of length 6,7,8 (defvar *testset*) ; testing strings from the grammar with foils (defvar *response*) ; response to a single string (defvar *allresponses*) ; collected responses (defvar *realtime*) ; realtime flag, nil = not realtime, window not shown (defvar *model-data*) ; results of 30 runs (setq *response* nil) ; response to single test (setq *allresponses* nil) ; list of responses (setq *item* nil) ; loop variable (setq *trials-left* 0) ; loop variable (setq *trainingset* '()) ; selected strings for training (setq *validset* '()) ; valid strings for testing (setq *foils* '()) ; foils used in testing (setq *testset* '()) ; test set of valid and invalid strings (setq *answerkey* '()) ; decodes testing set by valid or foil (setq *realtime* nil) ; run fast (nil) or in real time (T) ; set 30 run results to nil (setq *model-data* '(("rightOnValid" "rightOnInvalid" "wrongOnValid" "wrongOnInvalid"))) (setq *all-valid-strings* '( ("T" "T" "S") ("T" "T" "V" "X" "S") ("T" "T" "V" "X" "X" "S") ("T" "T" "V" "X" "X" "X" "S") ("T" "T" "V" "X" "X" "X" "X" "S") ("T" "P" "T" "S") ("T" "P" "T" "V" "X" "S") ("T" "P" "T" "V" "X" "X" "S") ("T" "P" "T" "V" "X" "X" "X" "S") ("T" "P" "P" "T" "S") ("T" "P" "P" "T" "V" "X" "S") ("T" "P" "P" "T" "V" "X" "X" "S") ("T" "P" "P" "P" "T" "S") ("T" "P" "P" "P" "T" "V" "X" "S") ("T" "P" "P" "P" "P" "T" "S") ("T" "P" "P" "P" "P" "P" "T" "S") ("T" "T" "V" "X" "P" "S") ("T" "T" "V" "X" "X" "P" "S") ("T" "T" "V" "X" "X" "X" "P" "S") ("T" "P" "T" "V" "X" "P" "S") ("T" "P" "T" "V" "X" "X" "P" "S") ("T" "P" "P" "T" "V" "X" "P" "S") ("T" "T" "V" "X" "P" "V" "X" "S") ;23 starting with "T" ("V" "X" "S") ("V" "X" "X" "S") ("V" "X" "X" "X" "S") ("V" "X" "X" "X" "X" "S") ("V" "X" "X" "X" "X" "X" "S") ("V" "X" "X" "X" "X" "X" "X" "S") ("V" "X" "P" "S") ("V" "X" "X" "P" "S") ("V" "X" "X" "X" "P" "S") ("V" "X" "X" "X" "X" "P" "S") ("V" "X" "X" "X" "X" "X" "P" "S") ("V" "X" "P" "V" "X" "S") ("V" "X" "X" "P" "V" "X" "S") ("V" "X" "X" "X" "P" "V" "X" "S") ("V" "X" "P" "V" "X" "X" "S") ("V" "X" "X" "P" "V" "X" "X" "S") ("V" "X" "P" "V" "X" "P" "S") ("V" "X" "X" "P" "V" "X" "P" "S") ("V" "X" "P" "V" "X" "X" "X" "S") ("V" "X" "P" "V" "X" "X" "P" "S") ("V" "X" "P" "V" "X" "X" "P" "S") ; 21 starting with "V", total 44 )) (print "loaded strings") ;======================== ; ; Present experiment ; ;======================= ;========================= (defun present-item (next) (clear-exp-window) (add-text-to-exp-window :text next :x (+ (random 50) 50) :y (+ (random 50) 50)) (proc-display) (print "processing item") ) ;========================== (defun present-string (str) ; shows string approximately in the middle of the screen (clear-exp-window) (setq n 0) (dolist (item str) (setq n (+ n 25)) (add-text-to-exp-window :text item :x (+ 50 n) :y 120) ) (proc-display) (print "processing string") ) ;======================== (defun remove-item (item) (remove-items-from-exp-window item) ) (print "loading model code") ;========================= ; generate foils ;========================= (defun generate-foil (len) (setf resp nil) (dotimes (i len) (setf num (random 1.0)) (if (<= num 0.2) (push "X" resp)) (if (and (> num 0.2) (<= num 0.4)) (push "S" resp)) (if (and (> num 0.4) (<= num 0.6)) (push "V" resp)) (if (and (> num 0.6) (<= num 0.8)) (push "P" resp)) (if (> num 0.8) (push "T" resp)) ) resp ) (print "loaded generate-foil") ;============================ ; randomize lists of strings ;============================ ;========================== (defun randomize-list (lst) (let ( (locallst lst) (lista '() ) (x nil) ) (dotimes (i (length lst) ) (setq x (nth (act-r-random (length locallst)) locallst)) ;(print (format nil "x= ~A, locallst= ~A" x locallst)) (push x lista) (setq locallst (remove x locallst)) );end times lista );end let ) ;=========================== ; merge valid and foils ;=========================== (defun merge-sets () (setq *testset* '() ) (setq *answerkey* '() ) (let ((localvalid *validset*) (localfoils *foils* ) ) (while (or (not (null localvalid)) (not (null localfoils)) ) (if (< (random 1.0) 0.5) ; add valid to test list (if (not (null localvalid)) (progn (setq *testset* (push (first localvalid) *testset*)) (setq localvalid (cdr localvalid)) (setq *answerkey* (push "yes" *answerkey*)) ) ); end if true statement ; add foil to test list (if (not (null localfoils)) (progn (setq *testset* (push (first localfoils) *testset*)) (setq localfoils (cdr localfoils)) (setq *answerkey* (push "no" *answerkey*)) ) ); end else statement ); end if ); end while (setq *testset* (concatenate 'list *testset* *testset*)) (setq *answerkey* (concatenate 'list *answerkey* *answerkey*)) );end let (print (format nil "testset = ~A" *testset*)) (print (format nil "answerkey = ~A" *answerkey*)) T) (print "loaded merge-sets") ;==================================== ; create training & testing sets ;==================================== (defun init-sets () (let ((working (randomize-list *all-valid-strings*))) (dotimes (i 18 T) ; 18 unique strings (progn (setq *trainingset* (push (first working) *trainingset*)) (setq working (cdr working)) ) ) (dotimes (i 22 T) ; 22 valid strings for testing (progn (setq *validset* (push (first working) *validset*)) (setq working (cdr working)) ) ) (dotimes (i 22) ; 22 foils (push (generate-foil (+ (act-r-Random 3) 5)) *foils*) ; length 6,7,8 ) (print (format nil "~A in training set = ~A" (length *trainingset*) *trainingset*)) (print (format nil "~A in valid set = ~A" (length *validset*) *validset*)) (print (format nil "~A foils = ~A" (length *foils*) *foils*)) (merge-sets) )T) (print "loaded shuffle and initialize functions") ;======================================= ; ; Training ; ; key points: ; 1. 18 valid strings ; 2. show 6 blocks of 3 valid strings 16 times ; 3. each shown for 5 seconds ; 4. gap of 0.6 seconds ; ;======================================= (defun train () (let ((window (open-exp-window "Training" :visible *realtime*))) ; watch (T) or fast (nil) (reset) (install-device window) (init-sets) ; init-valid-sets? (print (format nil "strings to train: ~A" *trainingset*)) (dotimes (i 6) ; 6 blocks of 3 (progn (setq trainingblock (list (first *trainingset*) (second *trainingset*) (third *trainingset*))) (setq *trainingset* (cdddr *trainingset*)) ; remove the 3 used (print (format nil "training block of 3 valid strings: ~A" trainingblock)) (dotimes (i 16) ; show block 16 times (dolist (oneseq trainingblock) (print (format nil "showing ~A" oneseq)) (present-string oneseq) (run 5 :real-time *realtime* ) ; real-time or fast (print (format nil "screen blank for 0.6 seconds")) (present-string '(" " " ")) ; blank screen (run 0.6 :real-time *realtime* ) ); end dolist ); end dotimes 16 (print (format nil "completed training on the training block: ~A" trainingblock)) ) ; end progn ) ; end dotimes 6 (print (format nil "completed training on the training set: ~A" *trainingset*)) )T) (print "loaded train") ;===================================================== ; ; Testing ; ; key points: ; 1. 22 strings from grammar (untrained) ; 2. 22 foils of length 6-8 ; 3. no time limit on display ; 4. no time limit on gap between displays ; 5. 44 strings randomized and each shown twice (2x) ; ;===================================================== (defun test () ; assumes prior training to create experimental window (present-string '("START" " ")) ; "*TEST" on screen to change mode (run 1 :real-time *realtime*) ; nil to run fast (setf *allresponses* nil) (setf *response* nil) (print (format nil "strings to test: ~A" *testset*)) (setq localsequence *testset*) (dolist (oneseq localsequence) (print (format nil "showing ~A" oneseq)) (present-string oneseq) (run 30 :real-time *realtime*) ; nil to run fast (print (format nil "response ~A" *response*)) (push *response* *allresponses*) ) ;end dolist (setq *allresponses* (reverse *allresponses*)) (print (format nil "testing complete.")) ; (print (format nil "tested sequences: ~A" *testset*)) ; (print (format nil "responses were: ~A" *allresponses*)) T) (print "loaded test") ;======================== ; ; Run experiment ; ;======================= ;======================= (defun run-set () (sgp :seed (100 0)) (dotimes (i 30) (print (format nil "experiment run ~A" i)) (do-experiment) ) (print (reverse *model-data*)) T) (print "loaded run-set") ;========================= (defun do-experiment () (setq *response* nil) (setq *allresponses* nil) (setq *item* nil) (setq *trials-left* 0) (setq *trainingset* '()) (setq *validset* '()) (setq *foils* '()) (setq *testset* '()) (setq *answerkey* '()) ; decodes testing set by valid or foil (train) (test) (analyze) (sgp :seed) ) (print "loaded do-experiment") ;==================== (defun analyze () (print (format nil "analyzing results")) (print (format nil "analyze>key= ~A" *answerkey*)) (print (format nil "analyze>ans= ~A" *allresponses*)) (let ((n (length *answerkey*)) (key *answerkey*) ; assumed same length as *allresponses* (ans *allresponses*) (rightOnValid 0) (rightOnInvalid 0) (wrongOnValid 0) (wrongOnInvalid 0) ) (dotimes (i n) (if (equal (first key) (first ans)) ;match (if (equal (first key) "yes") ;right on valid (setq rightOnValid (+ rightOnValid 1)) ;right on invalid (setq rightOnInvalid (+ rightOnInvalid 1)) ) ;non-match (if (equal (first key) "yes") ;missed valid (setq wrongOnValid (+ wrongOnValid 1)) ;missed invalid (setq wrongOnInvalid (+ wrongOnInvalid 1)) ) ) (setq key (cdr key)) (setq ans (cdr ans)) ) ; end loop (print (format nil "analyze>count= ~A of ~A" (+ rightOnValid rightOnInvalid) (length *allresponses*))) (print (format nil "analyze> or ~5F" (/ (+ rightOnValid rightOnInvalid) (length *allresponses*)))) (print (format nil "analyze>valid right ~A and invalid right ~A" rightOnValid rightOnInvalid)) (print (format nil "analyze>valid misses ~A and invalid misses ~A" wrongOnValid wrongOnInvalid)) (+ rightOnValid rightOnInvalid) (push (list rightOnValid rightOnInvalid wrongOnValid wrongOnInvalid) *model-data*) ); end let ) (print "loaded analyze") (print "supporting code loaded") ;================================================================ ; ; ; MODEL ; ; ;=============================================================== (clear-all) (define-model RPDM-1.0 (sgp :v nil ; no trace :trace-detail low ; trace level :rt 3.0 ; default is 0.0 :ans 0.1 ; noise value, default =0.0 :esc t ; enable subsymbolic computation :bll 0.5 ; base level learning :er T ; enable randomness :show-focus T ; if watching process, this helps see the steps ) (chunk-type guess task state last predict recognized) ; task is training or testing ; state controls sequencing of productions ; the idea is that by strengthening associations ; of last letter with a predicted next letter ; (bigrams), the system will learn to recognize ; the recognized slot is nil until prediction ; is evaluated, then it's either yes or no ; for each observed next letter (predicted or not) (add-dm (train isa chunk) (test isa chunk) (find isa chunk) (read isa chunk) (predict isa chunk) (start isa chunk) (stop isa chunk) (reconsider isa chunk) (end isa chunk) (training-goal isa guess task train) (testing-goal isa guess task test) ) (print "declared memory initiation") ;================================= ; Dual-use Perception Productions ;================================= (P start =goal> isa guess state nil ==> =goal> state find recognized nil ; clear any carryover +visual-location> isa visual-location screen-x lowest !output! (started and looking) ) (print "loaded rule start") ;===================== (p read-screen-search =goal> isa guess ; useful for both training & testing state start ?visual> state free ==> =goal> state find +visual-location> isa visual-location > screen-x current ; left to right reading screen-x lowest !output! (searching) ) (print "loaded rule read-screen-search") ;===================== (p read-screen-attend =goal> isa guess state find =visual-location> isa visual-location ?visual> state free ==> =goal> state read +visual> isa move-attention screen-pos =visual-location !output! (attending) ) (print "loaded rule read-screen-attend") ;========================================== ; Training-specific Perception Productions ;========================================== ;================== (P train=restart =goal> isa guess ; hardly any conditions, just goal isa guess and scene change task train - state find ; to prevent repeated firing ?visual> scene-change T ==> =goal> state find task train last nil ; clear any carryover predict nil ; clear any carryover recognized nil ; clear any carryover +visual> isa clear-scene-change +visual-location> isa visual-location screen-x lowest !output! (restarted and looking) ) (print "loaded rule train=restart") ;======================================== (p train=read-screen-search-failed-end =goal> isa guess task train state find ?visual-location> state error ==> =goal> predict "END" recognized nil ; to keep memory clean -goal> ; save this item +goal> isa guess task train state find ; at end, so start all over last nil predict nil recognized nil +visual-location> isa visual-location screen-x lowest !output! (at end) ) (print "loaded rule train=read-screen-search-failed-end") ;======================== (p train=associate-first =goal> isa guess task train state read last nil =visual> isa text value =letter ==> =goal> last =letter state find ; start +visual-location> isa visual-location > screen-x current ; left to right reading screen-x lowest !output! (saw first= =letter ) ) (print "loaded rule train=associate-first") ;================================== (p train=associate-next-not-at-end =goal> isa guess task train state read last =last =visual> isa text value =got - value "END" ==> =goal> state find ; start predict =got recognized nil ; keep memory clean -goal> +goal> isa guess task train state find ; start last =got +visual-location> isa visual-location > screen-x current ; left to right reading screen-x lowest !output! (saw next= =got) ) (print "loaded rule train=associate-next-not-at-end") ;========================================= ; Testing-specific Perception Productions ;========================================= ;=================== (P test=mode-change =goal> isa guess ; hardly any conditions, just goal isa guess and scene change task train =visual> isa text value "START" ==> =goal> state start task test last nil ; clear any carryover predict nil ; clear any carryover recognized nil ; clear any carryover -visual> +visual-location> isa visual-location screen-x lowest !output! (mode switched to testing) ) (print "loaded rule test=mode-change") ;================== (P test=restart =goal> isa guess ; hardly any conditions, just goal isa guess and scene change task test ; need to make a guess if here during testing ?visual> scene-change T ==> =goal> ; don't save goal when restarting - not useful info state find ; task test ; not needed because not changing and already tested last nil ; clear any carryover predict nil ; clear any carryover recognized nil ; clear any carryover +visual> isa clear-scene-change +visual-location> isa visual-location screen-x lowest !output! (restarted and looking) ) (print "loaded rule test=restart") ;=========================================== (p test=read-screen-search-failed-reporting =goal> isa guess task test state find recognized =resp ?visual-location> state error ==> =goal> predict "END" recognized nil ; to keep memory clean -goal> +goal> isa guess task test state start ;nil ; at end, so start all over last nil predict nil recognized nil +visual-location> isa visual-location screen-x lowest ; looks to far left with starting over -retrieval> !output! (at end responding with =resp) !eval! (setq *response* =resp) ) (print "loaded rule test=read-screen-search-failed-reporting") ;======================= (p test=associate-first =goal> isa guess task test state read last nil =visual> isa text value =letter ==> =goal> last =letter state find ; start +retrieval> isa guess task train last =letter - predict nil +visual-location> isa visual-location > screen-x current ; left to right reading screen-x lowest !output! (saw first= =letter attempting recall next) !eval! (setq *response* nil) ) (print "loaded rule test=associate-first") ;================================================================ ; Recognition Test Productions ;================================================================ ;================================= ; cases 1 no prediction, not done ;================================= ;==1a============================================================ (p test=1a=associate-next-prediction-error-not-at-end-reconsider =goal> isa guess task test state read last =last ?retrieval> state error ; error =visual> isa text value =got ; not at end ==> =goal> state reconsider predict =got -retrieval> +retrieval> isa guess task train last =last predict end !output! (saw next= =got with prediction error not end so reconsidering) ) (print "loaded rule test=1a=associate-next-prediction-error-not-at-end-reconsider") ;==1b======================================================== (p test=1b=associate-next-prediction-error-at-end-reconsider =goal> isa guess task test state read last =last ?retrieval> state error ; error ?visual-location> state error ; at end ==> =goal> state reconsider predict "END" -retrieval> +retrieval> isa guess task train last =last predict "END" !output! (saw next= end with prediction error at end so reconsidering) ) (print "loaded rule test=1b=associate-next-no-match-error-at-end") ;============================== ; cases type 2 - got predicted ;============================== ;==2a====================================== (p test=2a=associate-next-match-first-time =goal> isa guess task test state read last =last recognized nil ; first time =retrieval> isa guess last =last predict =got =visual> isa text value =got - value "END" ==> =goal> predict =got recognized nil ; to keep memory clean -goal> +goal> isa guess task test state find ;start last =got predict nil recognized "yes" +visual-location> isa visual-location > screen-x current ; left to right reading screen-x lowest -retrieval> +retrieval> isa guess task train last =got !output! (predicted =got right first time) ) (print "loaded rule test=2a=associate-next-match-first-time") ;==2b=================================================================== (p test=2b=associate-next-match-not-end=not-first=continue-recognitized =goal> isa guess task test state read last =last recognized =recog ; some value, so, not first - recognized nil =retrieval> isa guess last =last predict =got =visual> isa text value =got ; match - value "END" ; not end ==> =goal> predict =got recognized nil ; to keep memory clean -goal> +goal> isa guess task test state find ; start last =got predict nil recognized =recog ; continuing recognition +visual-location> isa visual-location > screen-x current ; left to right reading screen-x lowest -retrieval> +retrieval> isa guess task train last =got !output! (predicted =got match not first continue recognition) ) (print "loaded rule test=2b=associate-next-match-not-first-continue-recognition") ;==2e====================================================== (p test=2e=associate-next-match-at-end-report-recognitized =goal> isa guess task test state read last =last recognized =recog ; previous recognized status - recognized nil ?visual-location> state error ; at end =retrieval> isa guess last =last predict "END" ; match ==> =goal> predict "END" recognized nil ; to keep memory clean -goal> +goal> isa guess task test state nil ; at end, so start all over last nil predict nil recognized nil -retrieval> !output! (predicted= end match at end report =recog) !eval! (setq *response* =recog) ) (print "loaded rule test=2e=associate-next-match-at-end-report-recognized") ;=================================== ; cases type 3 predicted, but wrong ;=================================== ;==3a==================================================== (p test=3a=associate-next-no-match-not-at-end-reconsider =goal> isa guess task test state read last =last ; recognized ... don't care what previous results were, now some bad =retrieval> isa guess last =last predict =predict =visual> isa text value =got - value "END" - value =predict ==> =goal> state reconsider predict =got -retrieval> +retrieval> isa guess task train last =last predict =got !output! (saw next= =got but not as predicted so reconsidering) ) (print "loaded rule test=3a=associate-next-no-match-at-not-end-reconsider") ;================================ ; reconsidering ;================================ ; not at end ;==4a1============================================= (p test=4a1=reconsider-match-not-at-end-first-time =goal> isa guess task test state reconsider last =last predict =got - predict "END" recognized nil ; first time =retrieval> isa guess last =last predict =got ==> -retrieval> +retrieval> isa guess task train last =got =goal> state find ; start last =got predict nil recognized "yes" +visual-location> isa visual-location > screen-x current ; left to right reading screen-x lowest !output! (reconsidered match not at end first time) ) (print "loaded rule test=4a1=reconsidered-match-not-at-end-first-time") ;==4a2====================================================== (p test=4a2=reconsider-match-not-at-end-continue-recognized =goal> isa guess task test state reconsider last =last predict =got - predict "END" recognized =recog =retrieval> isa guess last =last predict =got ==> -retrieval> +retrieval> isa guess task train last =got =goal> state start last =got predict nil ; continue recognized !output! (reconsidered match not at end first time all good) ) (print "loaded rule test=4a2=reconsidered-match-not-at-end-first-time") ;==4b===================================== (p test=4b=reconsider-no-match-not-at-end =goal> isa guess task test state reconsider last =last predict =got - predict "END" ; recognized could be nil, yes, or no ?retrieval> state error ==> =goal> predict =got recognized nil ; to keep memory clean -goal> +goal> isa guess task test state find ; start last =got predict nil recognized "no" ; recognized set to "no" +visual-location> isa visual-location > screen-x current ; left to right reading screen-x lowest !output! (reconsidered no match not at end) ) (print "loaded rule test=4c=reconsidered-no-match-not-at-end") ; at end ;==4c================================================ (p test=4c=reconsider-match-at-end-report-recognized =goal> isa guess task test state reconsider last =last predict "end" recognized =resp ?visual-location> state error ; at end =retrieval> isa guess last =last predict "END" ==> -retrieval> -goal> +goal> isa guess task test state nil ; at end, so start all over last nil predict nil recognized nil !output! (reconsidered match at end) !eval! (setq *response* =resp) ) (print "loaded rule test=4c=reconsidered-match-at-end") ;==4d=========================================== (p test=4d=reconsider-no-match-at-end-report-no =goal> isa guess task test state reconsider last =last predict "END" ?retrieval> state error ?visual-location> state error ; at tnd ==> =goal> predict "END" recognized nil ; to keep memory clean -goal> +goal> isa guess task test state nil ; at end, so start all over last nil predict nil recognized "no" !output! (reconsidered no match at end) !eval! (setq *response* "no") ) (print "loaded rule test=4d=reconsidered-no-match-at-end-report-no") ;================================================ ; Model Initialization ;================================================ (setf *actr-enabled-p* t) ; nil = human t = model (goal-focus training-goal) ;================================================ ; my reminders ;================================================ (print "Use Notes:") (print "to initialize training & testing sets, use (init-sets)") (print "to train model, use (train)") (print "to test model, use (test)") (print "to run training, then testing, use (do-experiment)") (print "to run 30 experiments, use (run-set)") )