The experiments for this unit are a little more complex than the previous ones, but they do not use any new experiment generation functions. The fan experiment includes an alternate version of the model running function (it is commented out of the experiment and thus not used) that will be discussed.  The alternate function runs the model through the task without using the visual interface for input or the motor module for output.  Instead it puts the input into goal slots before running the model and reads the response from a goal slot upon completion.  This is done through the use of two functions that manipulate chunks outside of the model and such a mechanism can be used when the details of the visual/motor systems are not of interest or speed of running the model is really important (the assignment model works in such a fashion).

 

Here is the experiment code for the fan model.  This experiment differs from most that have been presented so far in that it only presents the test portion of the task.  The study portion has been encoded in the model explicitly and to do it as a person you need to first study the required items. For the model, the experiment will favor one of the two provided productions during two iterations over the task (once favoring one and then the other) so that the results can be averaged using only 2 runs.  This is another case of the experiment being simplified for the tutorial.  Another way to accomplish this would be to allow those two productions to compete equally and thus they would each fire on average half the time.  Then the model would be run for many iterations over the task and the results would be averaged to generate the predictions, but because there is no noise in the model and the only source of differences are foil trials this simplification is sufficient.

 

Here are the contents of the Misc window:

 

(defconstant *person-location-data* '(1.11 1.17 1.22

                                       1.17 1.20 1.22

                                       1.15 1.23 1.36

                                       1.20 1.22 1.26

                                       1.25 1.36 1.29

                                       1.26 1.47 1.47))

 

(defvar *response*)

(defvar *response-time*)

 

(defun test-sentence-model (person location target term)

  (let ((window (open-exp-window "Sentence Experiment"

                                 :visible t

                                 :width 600

                                 :height 300))

        (x 25))

   

    (reset)

    (pm-install-device window)

   

    (case term

      (person (spp retrieve-from-person :c 0))

      (location (spp retrieve-from-location :c 0)))

 

   (dolist (text (list "The" person "is" "in" "the" location))

     (add-text-to-exp-window :text text :x x :y 150 :width 75)

     (incf x 75))

   

    (setf *response* nil)

    (setf *response-time* nil)

   

    (pm-proc-display)

       

    (pm-run 30.0)

   

    (if (null *response*)

        (list 30.0 nil)

      (list *response-time*

            (or (and target (string-equal *response* "k"))

                (and (null target) (string-equal *response* "d")))))))

 

#| This version of the test-sentence-model function

   runs the model without using the visual interface.

   It is provided as an example of showing how one could

   bypass the interface when it isn't really necessary.

   The difference is explained in the experiment description

   text.

 

   The assignment for the unit MUST work with the original version

   NOT this one.

 

(defun test-sentence-model (person location target term)

 

  (reset)

 

  (case term

    (person (spp retrieve-from-person :c 0))

    (location (spp retrieve-from-location :c 0)))

 

  (mod-chunk-fct 'goal (list 'arg1 person 'arg2 location))

 

  (setf *response-time* (pm-run 30.0))

 

  (setf *response* (chunk-slot-value goal state))

 

  (list *response-time*

        (or (and target (string-equal *response* "k"))

            (and (null target) (string-equal *response* "d")))))

|#

 

(defun test-sentence-person (person location target term)

  (let ((window (open-exp-window "Sentence Experiment" :width 600 :height 300))

        (x 25)

        (start-time))

   

   (dolist (text (list "The" person "is" "in" "the" location))

     (add-text-to-exp-window :text text :x x :y 150)

     (incf x 75))

       

    (setf *response* nil)

    (setf *response-time* nil)

   

    (setf start-time (/ (pm-get-time) 1000.0))

   

    (while (null *response*) ;; wait for a key to be pressed by a person

           (allow-event-manager window))

   

    (list (- *response-time* start-time)

          (or (and target (string-equal *response* "k"))

              (and (null target) (string-equal *response* "d"))))))

 

 

(defmethod rpm-window-key-event-handler ((win rpm-window) key)

  (setf *response-time* (/ (pm-get-time) 1000.0))

  (setf *response* (string key)))

 

 

(defun do-person-location (term &optional (in-order *actr-enabled-p*))

  (let ((test-set '(("lawyer" "store" t)("captain" "cave" t)

                    ("hippie" "church" t)("debutante" "bank" t)

                    ("earl" "castle" t)("hippie" "bank" t)

                    ("fireman" "park" t)("captain" "park" t)

                    ("hippie" "park" t) ("fireman" "store" nil)

                    ("captain" "store" nil)("giant" "store" nil)

                    ("fireman" "bank" nil)("captain" "bank" nil)

                    ("giant" "bank" nil)("lawyer" "park" nil)

                    ("earl" "park" nil)("giant" "park" nil)))

        (results nil))

   

    (dolist (sentence (if in-order test-set (permute-list test-set)))

      (push (list sentence

                  (apply (if *actr-enabled-p*

                             #'test-sentence-model

                           #'test-sentence-person)

                         (append sentence (list term))))

            results))

    

    (mapcar #'second (sort results #'< :key #'(lambda (x)

                                                (position (car x) test-set))))))

 

(defun average-person-location ()

  (output-person-location (mapcar #'(lambda (x y)

                                      (list (/ (+ (car x) (car y)) 2.0)

                                            (and (cadr x) (cadr y))))

                            (do-person-location 'person)

                            (do-person-location 'location))))

 

(defun output-person-location (data)

  (let ((rts (mapcar 'first data)))

    (correlation rts *person-location-data*)

    (mean-deviation rts *person-location-data*)

    (format t "~%TARGETS:~%                         Person fan~%")

    (format t  "  Location      1             2             3~%")

    (format t "    fan")

   

    (dotimes (i 3)

      (format t "~%     ~d    " (1+ i))

      (dotimes (j 3)

        (format t "~{~8,3F (~3s)~}" (nth (+ j (* i 3)) data))))

   

    (format t "~%~%FOILS:")

    (dotimes (i 3)

      (format t "~%     ~d    " (1+ i))

      (dotimes (j 3)

        (format t "~{~8,3F (~3s)~}" (nth (+ j (* (+ i 3) 3)) data))))))

 

Here are the contents of the commands window:

 

(sgp :v t :esc t :lf 0.64 :mas 3)

 

(setf *actr-enabled-p* t)

 

(set-general-base-levels

 (guard* 10) (beach* 10) (castle* 10) (dungeon* 10) (earl* 10)

 (forest* 10) (hippie* 10) (park* 10) (church* 10) (bank* 10)

 (captain* 10) (cave* 10) (giant* 10) (debutante* 10) (fireman* 10)

 (lawyer* 10) (store* 10) (in* 10))

 

(goal-focus goal)

 

The only thing new there is the call to set-general-base-levels.  It is used to set the base level activation of those chunks (the Bi in the activation equation) to a "large" value so that they will not fail to be retrieved and their retrieval times will be essentially zero.

 

Now here are the definitions from the Misc window.  First it defines a global constant that holds the experimental data that the model is to be fit to.

 

(defconstant *person-location-data* '(1.11 1.17 1.22

                                      1.17 1.20 1.22

                                      1.15 1.23 1.36

                                      1.20 1.22 1.26

                                      1.25 1.36 1.29

                                      1.26 1.47 1.47))

 

Then it defines the global variables that will hold the response and the response time.

 

(defvar *response*)

(defvar *response-time*)

 

The test-sentence-model function takes 4 parameters.  The first, person, is the string of the person to display in the sentence.  The second, location, is the string of the location to display.  The third, target, is to be either t or nil to indicate whether this is a target or a foil trial respectively, and the forth, term, specifies which of the productions to favor in retrieving the study item and should be either the symbol person or location. The function returns a list of two items. The first is the response time of the key press in seconds or 30.0 if there was no response and the second item is t if the response was correct (k for a target and d for a foil) or nil if there was no response or the response was incorrect.

 

(defun test-sentence-model (person location target term)

 

First open a window.

 

  (let ((window (open-exp-window "Sentence Experiment"

                                 :visible t

                                 :width 600

                                 :height 300))

 

Create a variable to hold the x coordinates of the items to present.

 

        (x 25))

   

Reset the model and tell it which window to interact with.

 

    (reset)

    (pm-install-device window)

   

 

Set the cost of the requested production to 0 so that it will be favored.

 

    (case term

      (person (spp retrieve-from-person :c 0))

      (location (spp retrieve-from-location :c 0)))

 

Iterate over the words of the sentence and display each one 75 pixels from the previous one.

 

   (dolist (text (list "The" person "is" "in" "the" location))

     (add-text-to-exp-window :text text :x x :y 150 :width 75)

     (incf x 75))

   

Clear the response variables.

 

    (setf *response* nil)

    (setf *response-time* nil)

   

Make the model process the display and run for up to 30 seconds.

 

    (pm-proc-display)

       

    (pm-run 30.0)

   

If there was no response return the list of 30.0 and nil otherwise return the list of the response time in seconds and whether the response was correct or incorrect.

 

    (if (null *response*)

        (list 30.0 nil)

      (list *response-time*

            (or (and target (string-equal *response* "k"))

                (and (null target) (string-equal *response* "d")))))))

 

Next is a block of comments (the #| and |# are the start and end markers for a block of comments in Lisp) which contain a description of a function and its definition.  As indicated in the description this function is a replacement for test-sentence-model which was described above.  The version in the comments operates without using a display for interacting.

 

Using the replacement function will result in generating a somewhat different model than the one that works with the default function because it will not use the visual or motor modules, but the predictions can be made identical.  The model is probably easier to generate and will run faster without the overhead of generating a display, but does require extra work to estimate the “action” times since they will not be automatically produced by the respective modules.

 

Depending on the objectives of the modeling sometimes the actions aren’t really important and using a simplified representation or direct manipulation of the chunks is sufficient.  Prior to ACT-R 5, many models operated in such a manner.

 

#| This version of the test-sentence-model function

   runs the model without using the visual interface.

   It is provided as an example of showing how one could

   bypass the interface when it isn't really necessary.

   The difference is explained in the experiment description

   text.

 

   The assignment for the unit MUST work with the original version

   NOT this one.

 

(defun test-sentence-model (person location target term)

 

First, reset the model and set the production parameters to prefer the required retrieval production.

 

  (reset)

 

  (case term

    (person (spp retrieve-from-person :c 0))

    (location (spp retrieve-from-location :c 0)))

Next, modify the chunk named goal placing the strings directly into the slots.  The mod-chunk-fct function will be discussed in more detail below.

 

  (mod-chunk-fct 'goal (list 'arg1 person 'arg2 location))

 

Set the response time variable to the value returned by running the model.

 

  (setf *response-time* (pm-run 30.0))

 

Set the response to the value in the state slot of the chunk named goal.  The chunk-slot-value function (actually a macro) will be described below.

 

  (setf *response* (chunk-slot-value goal state))

 

Return the list of response time and correctness as above.

 

  (list *response-time*

        (or (and target (string-equal *response* "k"))

            (and (null target) (string-equal *response* "d")))))

|#

 

The test-sentence-person function operates like the test-sentence-model function except that the term parameter is ignored and it waits for a person to press a key instead of the model.

 

(defun test-sentence-person (person location target term)

  (let ((window (open-exp-window "Sentence Experiment" :width 600 :height 300))

        (x 25)

        (start-time))

   

   (dolist (text (list "The" person "is" "in" "the" location))

     (add-text-to-exp-window :text text :x x :y 150)

     (incf x 75))

       

    (setf *response* nil)

    (setf *response-time* nil)

   

    (setf start-time (/ (pm-get-time) 1000.0))

   

    (while (null *response*) ;; wait for a key to be pressed by a person

           (allow-event-manager window))

   

    (list (- *response-time* start-time)

          (or (and target (string-equal *response* "k"))

              (and (null target) (string-equal *response* "d"))))))

 

 

The key handler for the window just records the key press and the time of that press (in seconds) in the global variables.

 

(defmethod rpm-window-key-event-handler ((win rpm-window) key)

  (setf *response-time* (/ (pm-get-time) 1000.0))

  (setf *response* (string key)))

 

 

The do-person-location function takes one required parameter, term, which specifies which production to favor for the model as specified for test-sentence-model and one optional parameter, in-order, which defaults to the value of the global variable *actr-enabled-p*.  It iterates over all of the test sentences for the task collecting the data as returned by the test-sentence-model or test-sentence-person function.  If in-order is t the items are presented in the default order and if it is nil they are presented in a random order.  It returns a list of responses in the same order as the results in the global data list.

 

(defun do-person-location (term &optional (in-order *actr-enabled-p*))

(let ((test-set '(("lawyer" "store" t)("captain" "cave" t)

                    ("hippie" "church" t)("debutante" "bank" t)

                    ("earl" "castle" t)("hippie" "bank" t)

                    ("fireman" "park" t)("captain" "park" t)

                    ("hippie" "park" t) ("fireman" "store" nil)

                    ("captain" "store" nil)("giant" "store" nil)

                    ("fireman" "bank" nil)("captain" "bank" nil)

                    ("giant" "bank" nil)("lawyer" "park" nil)

                    ("earl" "park" nil)("giant" "park" nil)))

        (results nil))

   

    (dolist (sentence (if in-order test-set (permute-list test-set)))

      (push (list sentence

                  (apply (if *actr-enabled-p*

                             #'test-sentence-model

                            #'test-sentence-person)

                         (append sentence (list term))))

            results))

   

    (mapcar #'second (sort results #'< :key #'(lambda (x)

                                                (position (car x) test-set))))))

 

 

The average-person-location function takes no parameters.  It calls the do-person-location function once to favor the person and once to favor the location.  It returns a list of responses that are the average of the two response times for a given condition and t if both responses were correct.

 

(defun average-person-location ()

  (output-person-location (mapcar #'(lambda (x y)

                                      (list (/ (+ (car x) (car y)) 2.0)

                                            (and (cadr x) (cadr y))))

                            (do-person-location 'person)

                            (do-person-location 'location))))

 

 

The output-person-location function takes one parameter which is a list of responses that are in the same order as the global experimental data.  It prints the comparison of that data to the experimental results and prints a table of the response times for targets and foils.

 

(defun output-person-location (data)

  (let ((rts (mapcar 'first data)))

    (correlation rts *person-location-data*)

    (mean-deviation rts *person-location-data*)

    (format t "~%TARGETS:~%                         Person fan~%")

    (format t  "  Location      1             2             3~%")

    (format t "    fan")

   

    (dotimes (i 3)

      (format t "~%     ~d    " (1+ i))

      (dotimes (j 3)

        (format t "~{~8,3F (~3s)~}" (nth (+ j (* i 3)) data))))

   

    (format t "~%~%FOILS:")

    (dotimes (i 3)

      (format t "~%     ~d    " (1+ i))

      (dotimes (j 3)

        (format t "~{~8,3F (~3s)~}" (nth (+ j (* (+ i 3) 3)) data))))))

 

 

 

The two new functions (or macros) there are mod-chunk-fct and chunk-slot-value.  These are described in the ACT-R manual, but I’ll also discuss them here.  I’m going to start with a discussion of the difference between functions and macros in Lisp and why ACT-R uses one or the other in certain places and the impact that it has (if you don’t care to know that level of detail you can skip down to the descriptions of the new operators).

 

First, there is a distinction between functions and macros in Lisp.  When calling a function the parameters are evaluated first, but a macro does not evaluate its parameters (that’s not the only difference, but probably the most significant for current purposes).  Many of the ACT-R functions you’ve seen are actually macros e.g. chunk-type, add-dm, and p.  They are macros so that you don’t have to worry about quoting symbols or lists and other issues with Lisp syntax, but because they don’t evaluate their parameters there are some things that you can’t do with the macros (at least not without using an eval and backquoting but I’m not going to discuss backquoting either).  For example, say you have a variable called *number* and you’d like to create a chunk that has the value of *number* in one of its slots.  The following is not going to work:

 

CG-USER(39): (defvar *number* 3)

*NUMBER*

CG-USER(40): (chunk-type test slot)

TEST

CG-USER(41): (add-dm (foo isa test slot *number*))

(FOO)

 

because the macro isn’t going to evaluate the variable *number* to get its value but instead will create a chunk that literally contains *number* as shown here (wm is another ACT-R macro that prints out a chunk given its name):

 

CG-USER(42): (wm foo)

 Foo    0.000

    isa TEST

    slot *Number*

 

To get around that issue most of the ACT-R macros have a function that does the same thing (in fact most of the macros just expand to a call to the function) and the naming convention is to add “-fct” to the name for the functional form.  However, when using the function you have to pay more attention to Lisp syntax and make sure that symbols are quoted and lists are constructed appropriately, and often the required parameters are a little different – things that do not need to be in a list for the macro version need to be in a list for the function.  For example, add-dm-fct requires a list of lists as parameters instead of just an arbitrary number of lists.  Here is the code that would generate the chunk as desired above:

 

CG-USER(43): (add-dm-fct (list (list 'foo 'isa 'test 'slot *number*)))

(FOO)

CG-USER(44): (wm foo)

 Foo    0.000

    isa TEST

    slot 3

 

That’s not the only way to construct the lists (there are many ways one could accomplish the same thing) but should make the difference clear.

 

Now that I’ve covered that here are the new operators.

 

mod-chunk and mod-chunk-fctThis command modifies a chunk.  The macro version requires a chunk name and then any number of slot and value pairs and the function requires a chunk name and then a list of slot and value pairs.   Each of the slots specified for the chunk is given the corresponding value.  It returns the name of the chunk which was modified.  Here is an example of each being used on the chunk foo created above:

 

CG-USER(45): (mod-chunk foo slot 5)

FOO

CG-USER(46): (wm foo)

 Foo    0.000

    isa TEST

    slot 5

(FOO)

 

CG-USER(47): (mod-chunk-fct 'foo (list 'slot 6))

FOO

CG-USER(48): (wm foo)

 Foo    0.000

    isa TEST

    slot 6

(FOO)

 

chunk-slot-value and chunk-slot-value-fctThis command returns the value in a particular slot of a chunk.  The parameters for both versions are the name of the chunk and the name of the slot.  Here is an example of each again being used on the chunk foo:

 

 

 

CG-USER(49): (chunk-slot-value foo slot)

6

 

CG-USER(50): (chunk-slot-value-fct 'foo 'slot)

6

 

The Grouped model is really just a demonstration of partial matching.  The experiment code is only there to collect and display key presses of the model.  It is not an interactive experiment which a person can also do nor does it have a direct comparison to any existing data.

 

Here is the code from the Misc Window:

 

(defvar *response* nil)

 

(defun run-grouped-recall ()

    (setf *response* nil)

    (reset)

    (pm-run 20.0)

    (reverse *response*))

 

(defun record-response (value)

    (push value *response*))

 

and here is the code from the Commands Window:

 

(sgp :rt -.5 :ans .15 :ga 0 :esc t :pm t :v t :pmt t :act t)

 

(setf *actr-enabled-p* t)

 

(goal-focus goal)

 

(setsimilarities

 (first second -0.5)

 (second third -0.5)

 (first third -1))

 

 

Looking at the commands first we see basically all of the activation parameters being set:

 

(sgp :rt -.5 :ans .15 :ga 0 :esc t :pm t :v t :pmt t :act t)

 

The parameter :pm, partial matching, is set to t to enable that functionality, and :pmt, the partial matching trace, enables the printing of the details of the partial matching process during the run.

 

The next couple of lines are the same as have been seen many times before to set the model as the participant and set the initial contents of the goal buffer.

 

(setf *actr-enabled-p* t)

 

(goal-focus goal)

 

Then there is another new command for this unit, setsimilarities.

 

 

(setsimilarities

 (first second -0.5)

 (second third -0.5)

 (first third -1))

 

This command is used to set the similarity between pairs of chunks in ACT-R.  Those are the Mki values from the activation equation.  The parameters to setsimilarities (the macro form of the command) are an arbitrary number of three element lists.  The first two elements of a list must name chunks and the third value is the Mki between them, which is set symmetrically.

 

The experiment code in the Misc window is pretty simple.  First it defines a global variable *response* that will be used to hold the model’s responses.

 

(defvar *response* nil)

 

The run-grouped-recall function takes no parameters.  It clears the *response* list, runs the model, and then returns the list of responses in order. Because the responses are pushed onto the front of the list by the data collection function below it needs to be reversed before returning it.

 

 (defun run-grouped-recall ()

    (setf *response* nil)

    (reset)

    (pm-run 20.0)

    (reverse *response*))

 

The data collection is not done through pressing keys because there isn’t an interface to accept them.  Instead, the model simulates that process by directly calling a Lisp function to record the response.  How that is done will be described next.  This is the function that is called, and all it does is push the response onto the global list.

 

 

(defun record-response (value)

    (push value *response*))

 

Calling Lisp code from within a production is something new in this model, and it’s done in the harvest-first-item, harvest-second-item, and harvest-third-item productions.  Here is the harvest-third-item production.

 

(p harvest-third-item

   =goal>

      isa      recall-list

      step     recalling-item

      element  third

      group    =group

   =retrieval>

      isa      item

      name     =name

==>

   =retrieval>

      status   retrieved

   =goal>

      element  fourth

   +retrieval>

      isa      item

      parent   =group

      position fourth

    - status   retrieved

 

   !eval! (record-response =name)

)

 

The new operation shown in that production is !eval!. [The ‘!’ is called bang in Lisp, so that’s pronounced bang-eval-bang.]  It can be placed on either the LHS or RHS of a production and must be followed by a Lisp expression which will be evaluated. 

 

On the RHS of a production all it does is evaluate an expression.  When the production fires, the !eval! is just another action that occurs.  Note that the expression can contain variables from the production and the current binding of that variable in the instantiation is what will be used in the expression. 

 

However, on the LHS of a production a !eval! actually specifies a condition that must be met before the production can be selected as with all other LHS conditions.  The value returned by the evaluation of a LHS !eval! call must be non-nil for the production to be selected.  Because it will be called during the selection process, a LHS !eval! is going to be evaluated very often and even when the production that it’s in is not the one that will be eventually selected and fired.

 

!eval! is a powerful tool, but one that can easily be abused. Using it to call Lisp as an abstraction for an aspect of the model which isn’t necessary to model in detail for a particular task is the main recommended use.  In general, the predictions of the model shouldn’t depend heavily on the use of !eval!, otherwise there isn’t really any point to using ACT-R to model – you might as well just generate some functions to produce the data you want. 

 

In this model it is used to collect the model’s responses without needing the overhead of creating an experiment with which to interact.  Because this task isn’t concerned with response time or stimuli acquisition there isn’t really a need for an interactive experiment because it would only serve to increase the size and complexity of the model without adding anything to its purpose.

 

 

The assignment is the Siegler model which contains a “model only” experiment because there is no display with which to interact.  However, there aren’t any functions used which haven’t been explained so it should be easy to follow.

 

Here are the contents of the Misc Window:

 

(defvar *response*)

 

(defconstant *siegler-data* '((0   .05 .86  0  .02  0  .02  0   0  .06)

                              (0   .04 .07 .75 .04  0  .02  0   0  .09)

                              (0   .02  0  .10 .75 .05 .01 .03  0  .06)

                              (.02  0  .04 .05 .80 .04  0  .05  0   0)

                              (0    0  .07 .09 .25 .45 .08 .01 .01 .06)

                              (.04  0   0  .05 .21 .09 .48  0  .02 .11))

  "The experimental data to be fit")

 

(defun test-fact (arg1 arg2)

  (reset) 

  (mod-chunk-fct 'goal (list 'arg1 arg1 'arg2 arg2))

  (pm-run 30.0)

  (chunk-slot-value goal answer))

   

(defun do-one-set ()

  (list (test-fact "one" "one")

        (test-fact "one" "two")

        (test-fact "one" "three")

        (test-fact "two" "two")

        (test-fact "two" "three")

        (test-fact "three" "three")))

 

(defun run-subjects (n)

  (let ((responses nil))

    (dotimes (i n)

      (push (do-one-set) responses))

    (analyze responses)))

 

(defun analyze (responses)

  (let ((results (mapcar #'(lambda (x)

                            (mapcar #'(lambda (y)

                                       (/ y (length responses))) x))

                  (apply #'mapcar #'(lambda (&rest z)

                                  (let ((res nil))

                                   (dolist (i '("zero" "one" "two" "three" "four" "five" "six" "seven" "eight") res)

                                     (push (count i z :test #'string-equal) res)

                                     (setf z (remove i z :test #'string-equal)))

                                         (push (length z) res)

                                         (reverse res)))

                     responses))))

    (display-results results)))

 

(defun display-results (results)

  (let ((questions '("1+1" "1+2" "1+3" "2+2" "2+3" "3+3")))

    (correlation results *siegler-data*)

    (mean-deviation results *siegler-data*)

    (format t "       0     1     2     3     4     5     6     7     8   Other~%")

    (dotimes (i 6)

      (format t "~a~{~6,2f~}~%" (nth i questions) (nth i results)))))

      

and here is the code from the Commands Window:

 

(sgp :rt 0.0 :esc t :v nil :ga 0 :pm t :pmt nil :ans .1)

 

(set-general-base-levels

 (zero 10) (one 10) (two 10) (three 10) (four 10) (five 10)

 (six 10) (seven 10) (eight 10) (nine 10))

 

(setf *actr-enabled-p* t)

 

(goal-focus goal)

 

 

Everything in the Commands Window has also been seen before, so there is no need to go into detail about it and we will go right into the Misc Window code.

 

Start by defining a global variable to hold the response.

 

(defvar *response*)

 

Then define a global constant that holds the experimental data to be fit.

 

(defconstant *siegler-data* '((0   .05 .86  0  .02  0  .02  0   0  .06)

                              (0   .04 .07 .75 .04  0  .02  0   0  .09)

                              (0   .02  0  .10 .75 .05 .01 .03  0  .06)

                              (.02  0  .04 .05 .80 .04  0  .05  0   0)

                              (0    0  .07 .09 .25 .45 .08 .01 .01 .06)

                              (.04  0   0  .05 .21 .09 .48  0  .02 .11))

  "The experimental data to be fit")

 

 

The test-fact function takes two parameters which are the text of the items to present.

 

(defun test-fact (arg1 arg2)

 

The model is reset.

 

  (reset) 

 

The goal chunk is modified to contain the text items.

 

  (mod-chunk-fct 'goal (list 'arg1 arg1 'arg2 arg2))

 

Then the model is run to generate a response.

 

  (pm-run 30.0)

 

The model’s response is whatever is in the answer slot of the chunk named goal, and that value is returned.

 

  (chunk-slot-value goal answer))

 

 

The do-one-set function takes no parameters.  It runs one trial of each of the 6 stimuli and returns the list of the responses.

 

   

(defun do-one-set ()

  (list (test-fact "one" "one")

        (test-fact "one" "two")

        (test-fact "one" "three")

        (test-fact "two" "two")

        (test-fact "two" "three")

        (test-fact "three" "three")))

 

 

The run-subjects function takes one parameter which is the number of times to repeat the set of 6 test stimuli.  It runs that many times over the set of stimuli and then outputs the results.

 

 

(defun run-subjects (n)

  (let ((responses nil))

    (dotimes (i n)

      (push (do-one-set) responses))

    (analyze responses)))

 

 

The analyze function takes one parameter which is a list of lists where each sublist contains the responses to the 6 test stimuli.  It calculates the percentage of each of the answers from zero to eight or other from the given trial data and calls display-results to print out that information.

 

(defun analyze (responses)

  (let ((results (mapcar #'(lambda (x)

                            (mapcar #'(lambda (y)

                                       (/ y (length responses))) x))

                  (apply #'mapcar #'(lambda (&rest z)

                                  (let ((res nil))

                                   (dolist (i '("zero" "one" "two" "three" "four" "five" "six" "seven" "eight") res)

                                     (push (count i z :test #'string-equal) res)

                                     (setf z (remove i z :test #'string-equal)))

                                         (push (length z) res)

                                         (reverse res)))

                     responses))))

    (display-results results)))

 

 

The display-results function takes one parameter which is a list of six lists where each sublist is a list of nine items which represents the percentages of the answers 0-8 or other for each of the 6 test stimuli.  It prints out the comparison to the experimental data and then displays the table of the results.

 

(defun display-results (results)

  (let ((questions '("1+1" "1+2" "1+3" "2+2" "2+3" "3+3")))

    (correlation results *siegler-data*)

    (mean-deviation results *siegler-data*)

    (format t "       0     1     2     3     4     5     6     7     8   Other~%")

    (dotimes (i 6)

      (format t "~a~{~6,2f~}~%" (nth i questions) (nth i results)))))