;;;
;;; ACT-R/PM 4.0 model of the sperling task ;;; ;;; to run the model call ;;; (run-sperling-sim n rt?) ;;; where n is the number of runs, and ;;; rt? is either t or nil, to specify ;;; whether attention times should be random or not (defvar *exp*) (defvar *response*) (defvar *run-step*) (defvar *duration*) (defvar *sound-encode*) (defvar *tone*) (defvar *answer-row*) (defvar *text*) (defvar *graphic*) (defvar *overlay*) (defvar *v*) (defvar *runs*) (defvar *attention*) (defvar *random-times*) (setf *duration* .81) (setf *sound-encode* .15) (setf *answer-row* nil) (setf *run-step* nil) (setf *response* nil) (setf *v* nil) (setf *runs* 1) (setf *text* t) (setf *graphic* nil) (setf *overlay* nil) (setf *attention* .185) (setf *random-times* t) (defparameter *sperling-tone-times* '(-.05 0 .15 .3 1.0)) (defparameter *sperling-data* (list '(-.05 3.3) (list 0 (/ 9.1 3)) '(.15 2.4) (list .3 (/ 6.1 3)) '(1.0 1.5))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; ;;; 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 "Sperling Experiment Model" 2) (:table) (:table) "Image duration: " (:string :sy *duration* 0.81) (:new-row) "Time to encode sound: " (:string :sy *sound-encode* .15) (:new-row) "Move attention time: " (:string :sy *attention* .185) (:new-row) "Number of runs (1-100): " (:string :sy *runs* 1) (:new-row) (:checkbox "Randomness in attention times" :sy *random-times* t) (:table-end) (:table) (:checkbox "Trace" :sy *v* nil) (:new-row) (:checkbox "Text output" :sy *text* t) (:new-row) (:checkbox "Graphic output" :sy *graphic* nil) (:new-row) (:checkbox "Show simulation and experiment data on one graph" :sy *overlay* nil) (:table-end) (:table-end) (:new-para) (:heading "Model not currently available" 2) #| (:button "Show Experiment Results" "(display-sperling *sperling-data* nil)") (:new-para) (:button "Run model" "(if (and (numberp *duration*) (numberp *sound-encode*) (numberp *attention*) (numberp *runs*)) (run-sperling-sim (min 100 (max 1 *runs*)) *random-times*) (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 10 runs of the model" (:new-line) "- The trace of 1 run is approximatly 20k (12 pages) in size" |# (:new-para))) (defun playtone-sperling () (if *actr-enabled-p* (mod-chunk-fct 'goal (list 'tone *tone*)) (cond ((equal *tone* 'low) (beep 3)) ((equal *tone* 'mid) (beep 2)) ((equal *tone* 'high) (beep 1))))) (defun setup-sperling () (let* ((d (make-instance 'dialog :window-title "Experiment Setup" :view-size #@(310 200))) (t1-txt (make-instance 'static-text-dialog-item :view-position #@(10 10) :dialog-item-text "Enter the duration before tone:")) (t1-inp (make-instance 'editable-text-dialog-item :view-position #@(230 10) :view-size #@(50 16))) (t2-txt (make-instance 'static-text-dialog-item :view-position #@(10 50) :dialog-item-text "Enter the duration after tone:")) (t2-inp (make-instance 'editable-text-dialog-item :view-position #@(220 50) :view-size #@(50 16))) (subj-txt (make-instance 'static-text-dialog-item :view-position #@(10 85) :dialog-item-text "Subject:")) (s1 (make-instance 'radio-button-dialog-item :view-position #@(80 85) :radio-button-cluster 'subj :dialog-item-text "human")) (s2 (make-instance 'radio-button-dialog-item :view-position #@(150 85) :radio-button-cluster 'subj :dialog-item-text "ACT-R")) (step-txt (make-instance 'static-text-dialog-item :view-position #@(10 110) :dialog-item-text "Stepping:")) (st1 (make-instance 'radio-button-dialog-item :view-position #@(80 110) :radio-button-cluster 'step :dialog-item-text "off")) (st2 (make-instance 'radio-button-dialog-item :view-position #@(150 110) :radio-button-cluster 'step :dialog-item-text "on")) (trace-txt (make-instance 'static-text-dialog-item :view-position #@(10 130) :dialog-item-text "Tracing:")) (tr1 (make-instance 'radio-button-dialog-item :view-position #@(80 130) :radio-button-cluster 'trace :dialog-item-text "off")) (tr2 (make-instance 'radio-button-dialog-item :view-position #@(150 130) :radio-button-cluster 'trace :dialog-item-text "on")) (stop nil) (b (make-instance 'button-dialog-item :view-position #@(120 160) :default-button t :dialog-item-text " OK " :dialog-item-action #'(lambda (x) x (setf stop t))))) (add-subviews d t1-txt t1-inp t2-txt t2-inp subj-txt s1 s2 step-txt st1 st2 trace-txt tr1 tr2 b) (view-draw-contents d) (event-dispatch) (loop (if (radio-button-pushed-p s2) (setf *actr-enabled-p* t) (setf *actr-enabled-p* nil)) (if (radio-button-pushed-p st2) (setf *run-step* t) (setf *run-step* nil)) ;(if (radio-button-pushed-p tr2) (setf *print-viewed* t) ; (setf *print-viewed* nil)) (when (and stop (not (equal (dialog-item-text t2-inp) ""))) (window-close d) (return))) (reset) (pm-reset) (eval (read-from-string (format nil "(experiment-sperling ~a '~a ~a)" (float (read-from-string (dialog-item-text t1-inp))) (if (zerop (random 2)) 'low 'high) (float (read-from-string (dialog-item-text t2-inp))) ))) )) (defclass exp-sperling (dialog) ((text1 :accessor text1 :initform (make-instance 'static-text-dialog-item :view-position #@(75 100) :view-font '("courier" 17 :bold))) (text2 :accessor text2 :initform (make-instance 'static-text-dialog-item :view-position #@(125 100) :view-font '("courier" 17 :bold))) (text3 :accessor text3 :initform (make-instance 'static-text-dialog-item :view-position #@(175 100) :view-font '("courier" 17 :bold))) (text4 :accessor text4 :initform (make-instance 'static-text-dialog-item :view-position #@(225 100) :view-font '("courier" 17 :bold))) (text5 :accessor text5 :initform (make-instance 'static-text-dialog-item :view-position #@(75 150) :view-font '("courier" 17 :bold))) (text6 :accessor text6 :initform (make-instance 'static-text-dialog-item :view-position #@(125 150) :view-font '("courier" 17 :bold))) (text7 :accessor text7 :initform (make-instance 'static-text-dialog-item :view-position #@(175 150) :view-font '("courier" 17 :bold))) (text8 :accessor text8 :initform (make-instance 'static-text-dialog-item :view-position #@(225 150) :view-font '("courier" 17 :bold))) (text9 :accessor text9 :initform (make-instance 'static-text-dialog-item :view-position #@(75 200) :view-font '("courier" 17 :bold))) (text10 :accessor text10 :initform (make-instance 'static-text-dialog-item :view-position #@(125 200) :view-font '("courier" 17 :bold))) (text11 :accessor text11 :initform (make-instance 'static-text-dialog-item :view-position #@(175 200) :view-font '("courier" 17 :bold))) (text12 :accessor text12 :initform (make-instance 'static-text-dialog-item :view-position #@(225 200) :view-font '("courier" 17 :bold))) (text13 :accessor text13 :initform (make-instance 'static-text-dialog-item :view-position #@(100 100) :view-font '("courier" 17 :bold))) (text14 :accessor text14 :initform (make-instance 'static-text-dialog-item :view-position #@(100 100) :view-font '("courier" 17 :bold))) (text15 :accessor text15 :initform (make-instance 'static-text-dialog-item :view-position #@(100 100) :view-font '("courier" 17 :bold))) (text16 :accessor text16 :initform (make-instance 'static-text-dialog-item :view-position #@(100 100) :view-font '("courier" 17 :bold)))) (:default-initargs :view-size #@(300 300) :window-title "Experiment")) (defun pre-exp-sperling () (message-dialog "Ready to hear low tone?") (setf *tone* 'low) (playtone-sperling) (message-dialog "Ready to hear middle tone?") (setf *tone* 'mid) (playtone-sperling) (message-dialog "Ready to hear high tone?") (setf *tone* 'high) (playtone-sperling) (message-dialog "Ready for experiment?")) (defmethod view-key-event-handler ((self exp-sperling) char) (cond ((equal char #\newline) (setf *response* 'return)) ((equal char #\space) (setf *response* 'space)) ((equal char #\.) (setf *response* 'period)) (t (setf *response* (read-from-string (string char)))))) (defun event-sperling (spec) (setf *response* nil) (loop (event-dispatch 0) (when *actr-enabled-p* (pm-run 1)) (cond ((listp spec) (when (member *response* spec) (return))) (t (cond ((equal spec 'nospace) (when (and *response* (not (equal *response* 'space))) (return))) (t (when *response* (return)))))) (when (and *actr-enabled-p* (zerop *cycle*)) (return)))) (defun display-screen-sperling () (let ((txt)) (without-interrupts (dotimes (i 12) (setf txt (eval (eval `(list (read-from-string (format nil "text~a" ,(1+ i))) ,*exp*)))) (add-subviews *exp* txt)) (event-dispatch)) (when *actr-enabled-p* (cleartype 'visual-object) (pm-proc-display)))) (defun clear-screen-sperling () (setf *answer-row* nil) (let ((txt)) (dotimes (i 4) (setf txt (eval (eval `(list (read-from-string (format nil "text~a" ,(cond ((eq *tone* 'high) (1+ i)) ((eq *tone* 'mid) (+ 5 i)) ((eq *tone* 'low) (+ 9 i))))) ,*exp*)))) (push (read-from-string (dialog-item-text txt)) *answer-row*)) (setf *answer-row* (reverse *answer-row*)) (without-interrupts (dotimes (i 12) (setf txt (eval (eval `(list (read-from-string (format nil "text~a" ,(1+ i))) ,*exp*)))) (set-dialog-item-text txt ""))) (when *actr-enabled-p* (pm-proc-display)))) (defun set-sperling-params () (sgp-fct (list :era t :er nil :v *v*)) ;(pm-set-params :visual-attention-latency (- *attention* .05) :randomize-time *random-times*) (set-pm-params-mth *mp* (list :visual-attention-latency (- *attention* .05) :randomize-time *random-times*)) (parameters-fct 'pick-bottom-row (list :effort *sound-encode*)) (parameters-fct 'pick-top-row (list :effort *sound-encode*)) (parameters-fct 'pick-middle-row (list :effort *sound-encode*))) (defun experiment-sperling (tone tone-time screen-on-time screen-off-time) (when *v* (format *standard-output* "tone on: ~S screen-on: ~s screen off: ~S~%" tone-time screen-on-time screen-off-time)) (let* ((lis '(b c d f g h j k m n p q r s t v w x y z)) (ans-lis nil) (report-txt (make-instance 'static-text-dialog-item :view-position #@(50 100) :dialog-item-text (format nil "Report letters in ~a row:" tone))) (report-inp (make-instance 'static-text-dialog-item :view-position #@(50 150) :dialog-item-text "" :view-size #@(200 17))) (b (make-instance 'button-dialog-item :view-position #@(120 260) :dialog-item-text " OK " :default-button t)) (score 0) (r nil) (txt nil)) (setf *tone* tone) (unless *actr-enabled-p* (pre-exp-sperling)) (reset) (pm-reset) (set-sperling-params) (mod-chunk-fct 'goal (list 'guess (case (random 3) (0 'high) (1 'mid) (2 'low)))) (setf *exp* (make-instance 'exp-sperling)) (pm-install-device *exp*) (dotimes (i 12) (setf txt (eval (eval `(list (read-from-string (format nil "text~a" ,(1+ i))) ,*exp*)))) (setf r (random (length lis))) (set-dialog-item-text txt (string (nth r lis))) (setf lis (remove (nth r lis) lis))) (if (zerop screen-on-time) (if (zerop tone-time) (progn (display-screen-sperling) (playtone-sperling) (pm-delayed-event screen-off-time #'clear-screen-sperling)) (progn (display-screen-sperling) (pm-delayed-event tone-time #'playtone-sperling) (pm-delayed-event screen-off-time #'clear-screen-sperling))) (if (zerop tone-time) (progn (playtone-sperling) (pm-delayed-event screen-on-time #'display-screen-sperling) (pm-delayed-event screen-off-time #'clear-screen-sperling)) (progn (pm-delayed-event tone-time #'playtone-sperling) (pm-delayed-event screen-on-time #'display-screen-sperling) (pm-delayed-event screen-off-time #'clear-screen-sperling)))) (pm-run 10) (mod-chunk-fct 'report (list 'report-y (case *tone* (high 110) (mid 160) (low 210)) 'report-x nil)) (goal-focus report) (add-subviews *exp* report-txt report-inp b) (event-dispatch) (setf *response* nil) (loop (event-sperling 'nospace) (cond ((equal *response* 'return) (return)) (t (set-dialog-item-text report-inp (format nil "~a ~a" (dialog-item-text report-inp) *response*)) (setf *response* nil) (event-dispatch)))) (setf ans-lis (read-from-string (format nil "(~a)" (dialog-item-text report-inp)))) (dotimes (i (length ans-lis)) (if (member (nth i ans-lis) *answer-row*) (incf score))) (window-close *exp*) (setf *exp* nil) (when *v* (format t "~&~a~&~a~&~3,3f~&~%SCORE = ~s~%" *answer-row* ans-lis (/ score (length *answer-row*)) score)) score)) (defun display-sperling (data simulation) (let ((s-data (sort data #'< :key #'car))) (when simulation (format *standard-output* "~%~%Parameters for run: (~S ~S ~S ~S ~S)~%~%" *duration* *sound-encode* *attention* *runs* *random-times*)) (when *text* (format *standard-output* "~a Data:~%~%Tone time Average number of items recalled~%" (if simulation "Simulation" "Experimental")) (dolist (j s-data) (format *standard-output* "~5,2f ~5,2f~%" (car j) (second j))) (when (and simulation *overlay*) (format *standard-output* "~%~%Experimental Data:~%~%Tone time Average number of items recalled~%") (dolist (j *sperling-data*) (format *standard-output* "~5,2f ~5,2f~%" (car j) (second j))))) (when *graphic* (format *standard-output* " ")))) (defun results-list-sperling () (let ((res nil)) (dolist (i *sperling-tone-times*) (setf res (cons (list i 0) res))) res)) (defun run-sperling-sim (runs random-times) (setf *actr-enabled-p* t) (setf *random-times* random-times) (let ((res (results-list-sperling))) (dotimes (i runs) (dolist (j *sperling-tone-times*) (incf (second (assoc j res)) (experiment-sperling (case (random 3) (0 'high) (1 'mid) (2 'low)) (cond ((< j 0) 0) (t j)) (cond ((< j 0) (- j)) (t 0)) (cond ((< j 0) (- *duration* j)) (t *duration*)))))) (display-sperling (mapcar #'(lambda (x) (list (first x) (/ (second x) runs))) res) t))) (defun wait-sperling (n) (if *actr-enabled-p* (pm-run n) (let ((start (get-internal-real-time))) (loop (if (> (/ (- (get-internal-real-time) start) 1000) n) (return)))))) #| (defun run-step-sperling (x) x (when (and *actr-enabled-p* *run-step*) (setf *response* nil) (format t "~&PRESS IN EXPERIMENT WINDOW TO CONTINUE...~&") (loop (event-dispatch) (when (equal *response* 'space) (setf *response* nil) (return))))) (setf *conflict-set-hook-fn* 'run-step-sperling) |# ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; ;;; The ACT-R/PM model (clearall) (pm-add-types-and-chunks) (sgp :era t :er nil :v t) (WMEType encode-letter tone report-y guess) (WMEType report-row report-y object-buffer report-x) (chunk-type order current next) (addwm (goal isa encode-letter) (report isa report-row) (high isa chunk) (mid isa chunk) (low isa chunk) (seen isa chunk) (done isa chunk) (first isa order current 81 next 131) (second isa order current 131 next 181) (third isa order current 181 next 231) (fourth isa order current 231 next done) (lowest isa chunk)) (wmfocus goal) ;; if tone is high, look for top row (P attend-top-row =goal> ISA encode-letter tone high report-y =y =loc> isa visual-location time now attended nil screen-x lowest screen-y =y =state> isa module-state module :vision modality free ==> !send-command! :vision move-attention :location =loc !output! ("Tone HIGH - Top row starts ~s~&" =loc) ) ;; if tone is high, look for top row (P attend-middle-row =goal> ISA encode-letter tone mid report-y =y =loc> isa visual-location time now attended nil screen-x lowest screen-y =y =state> isa module-state module :vision modality free ==> !send-command! :vision move-attention :location =loc !output! ("Tone mid - Middle row starts ~s~&" =loc) ) ;(parameters attend-top-row :effort .2) ;; if tone is low, look for bottom row (P attend-bottom-row =goal> ISA encode-letter tone low report-y =y =loc> isa visual-location time now attended nil screen-x lowest screen-y =y =state> isa module-state module :vision modality free ==> !send-command! :vision move-attention :location =loc !output! ("Tone LOW - Bottom row starts ~s~&" =loc) ) ;(parameters attend-bottom-row :r .9) ; :r .8 :effort .2) (p pick-bottom-row =goal> isa encode-letter report-y nil tone low ==> =goal> report-y 210) (parameters-fct 'pick-bottom-row (list :effort *sound-encode*)) (p pick-top-row =goal> isa encode-letter report-y nil tone high ==> =goal> report-y 110) (parameters-fct 'pick-top-row (list :effort *sound-encode*)) (p pick-middle-row =goal> isa encode-letter report-y nil tone mid ==> =goal> report-y 160) (parameters-fct 'pick-middle-row (list :effort *sound-encode*)) ;; if visual-features gone, report visual-objects in row (P start-report =goal> ISA report-row report-y =y report-x nil =state1> isa module-state module :vision modality free =state2> isa module-state module :motor modality free ==> =newgoal> ISA report-row report-y =y report-x 81 !focus-on! =newgoal ) (parameters start-report :r .2) ;; pick visual-object out of buffer that's in row (P report-letter =goal> ISA report-row report-y =y report-x =x =spos> isa visual-location screen-y =y screen-x =x =item> ISA visual-object status nil screen-pos =spos value =val =state> isa module-state module :motor modality free =order> isa order current =x next =next ==> !output! ("Pressing ~a" =val) !send-command! :motor press-key =val =item> status seen =goal> report-x =next ) (P skip-letter =goal> ISA report-row report-y =y report-x =x =state> isa module-state module :motor modality free =order> isa order current =x next =next ==> =goal> report-x =next ) (parameters skip-letter :r 0.5) (p done-report =goal> isa report-row report-x done report-y =y =state> isa module-state module :motor modality free ==> !output! ("queue: ~a" (sched-q *mp*)) !send-command! :motor press-key "return" =goal> report-y nil ) (parameters done-report :r .1) (P default-attend-letter =goal> ISA encode-letter report-y nil =loc> isa visual-location time now attended nil screen-x lowest screen-y lowest =state> isa module-state module :vision modality free ==> !send-command! :vision move-attention :location =loc ) (parameters default-attend-letter :r .5) ; :effort .2) (P default-attend-letter-guess-low =goal> ISA encode-letter guess low report-y nil =loc> isa visual-location time now attended nil screen-x lowest screen-y 210 =state> isa module-state module :vision modality free ==> !send-command! :vision move-attention :location =loc ) (Parameters default-attend-letter-guess-low :r .9) (P default-attend-letter-guess-middle =goal> ISA encode-letter guess mid report-y nil =loc> isa visual-location time now attended nil screen-x lowest screen-y 160 =state> isa module-state module :vision modality free ==> !send-command! :vision move-attention :location =loc ) (Parameters default-attend-letter-guess-middle :r .9) (P default-attend-letter-guess-high =goal> ISA encode-letter guess high report-y nil =loc> isa visual-location time now attended nil screen-x lowest screen-y 110 =state> isa module-state module :vision modality free ==> !send-command! :vision move-attention :location =loc ) (Parameters default-attend-letter-guess-high :r .9)