3 Commits 0ef01fcf5a ... d2b8b798b2

Autor SHA1 Mensaje Fecha
  Peter Lane d2b8b798b2 added code for ECVP 2023 poster hace 11 meses
  Peter Lane cd308c2c0f added code for SGAI 2023 paper hace 11 meses
  Peter Lane 7143f14b97 added README with licence hace 11 meses

+ 26 - 0
README.md

@@ -0,0 +1,26 @@
+# Examples of Using GEMS
+
+This repository holds a collection of examples of using GEMS.
+
+## MIT Licence
+
+Copyright (c) the individual authors.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+

+ 6 - 0
published-results/ecvp-2023-posner/README.md

@@ -0,0 +1,6 @@
+# ECVP 2023: Generating Candidate Cognitive Models
+
+This folder contains the code for the experimental results 
+reported in:
+
+* L.K. Bartlett, A. Pirrone, N. Javed, P.C.R. Lane and F. Gobet, Generating candidate cognitive models for the Posner cueing task [Conference poster]. _45^th^ European Conference on Visual Perception_, Paphos, Cyprus, 2023.

+ 48 - 0
published-results/ecvp-2023-posner/Run-posner.lisp

@@ -0,0 +1,48 @@
+#|
+This code is to easily go through all the posner experiments
+|#
+
+;; 1. Set stuff up
+(setf *default-pathname-defaults*
+      ;;#P"/home/laurab/common-lisp/gems-1.2-alpha-3/posner/")
+      #P"/Users/bartletl/Documents/portacle/projects/gems-1.2-alpha-3/posner/")
+(require :asdf)
+(require :gems)
+
+;; 2. Enter the names of the experiments to be included
+(setf experiment-list (list 'arjona))
+
+;; 3. Go through each and run GP
+(load "posner-initialise.lisp")
+
+;; optional things:
+(setf decay-toggle 0) ;; stm decay on (1) or off (0)
+(setf *decay-threshold* 0.1) ;; just try different values
+
+(defparameter stored-state (make-random-state t)) ;; this is a new one every time. so need to actually store it somewhere.
+(setf *random-state* (make-random-state stored-state))
+;; this info will be printed in the output file and so can be used in future.
+
+
+(dolist (name-exp experiment-list)
+  (load (format nil "posner-~a.lisp" name-exp))
+  (defun operator-set () op-set)
+
+
+  (run-gp :f-a 0.7 ;; changed from 0.75
+          :f-t 0.25
+          :f-s 0.05 ;;changed from 0.05
+          :population-size 5000 ;; change from 3000 ; 8000 ; 2000
+          :max-generations 500 ;; change from 200 ; 700 ; 500
+          :phased t
+          :logger (gems:combine-loggers (gems:make-logger (format nil "summary-~a-prev.csv" name-exp) :if-exists :supersede) (gems:make-logger (format nil "models-~a-prev.yml" name-exp) :kind :trace :filter #'(lambda (generation-number) (= generation-number *max-generations*)))))
+)
+
+(defun operator-set () (append op-set wait-op-set))
+(load (format nil "posner-~a.lisp" 'arjona))
+
+(setf clean-models-arjona (best-models "models-arjona.yml"))          
+(print (format nil "~a final models - ~a" (length clean-models-arjona) 'Arjona))
+
+
+

+ 168 - 0
published-results/ecvp-2023-posner/posner-ARJONA.lisp

@@ -0,0 +1,168 @@
+#| set up inputs |#
+
+(setf input-1 (make-input :type :cue :location :centre :direction nil :store-time 0 :modality :visual))
+(setf input-2 (make-input :type :target :location nil :direction nil :store-time 0 :modality :auditory))
+
+(setf input-nil (make-input :location nil :direction nil :type nil :store-time 0 :modality nil))
+
+(setf test-input-1 (make-input :type :cue :location :centre :direction :left :store-time 0 :modality :visual))
+(setf test-input-2 (make-input :type :target :location :left :direction nil :store-time 0 :modality :auditory))
+(setf test-input-3 (make-input :type :target :location :right :direction nil :store-time 0 :modality :auditory))
+
+
+(defun get-cue-validity (input-pair)
+  (if (equal (first input-pair)
+             (second input-pair))
+      "V" "I")) 
+
+
+#| arjona specifics |#
+
+(setf *response-window* '(970 2070))
+(setf ntrials 100)
+(setf *strength-assoc* .5)
+(setf *cue-probs* '(.86 .68 .5))
+
+(defun create-stimuli (ntrials cue-validity-prob)    ;;; creates a list of n cue-stimulus pairs
+  (let* ((temp-list nil)
+         (valid-n (round (* cue-validity-prob ntrials)))
+         (invalid-n (- ntrials valid-n)))
+    (append temp-list (make-list (/ valid-n 2) :initial-element '(:left :left))
+            (make-list (/ valid-n 2) :initial-element '(:right :right))
+            (make-list (/ invalid-n 2) :initial-element '(:left :right))
+            (make-list (/ invalid-n 2) :initial-element '(:right :left)))))
+
+;;(setf *cue-stimuli-list* (create-stimuli ntrials cue-validity))
+
+;; target-response
+
+;; timeline
+(defun timeline (time)
+  (cond
+    ((and (>= time 0) (< time 300)) 'NIL)
+    ((and (>= time 300) (< time 600)) '((stim1 centre)))
+    ((and (>= time 600) (< time 970)) 'NIL)
+    ((and (>= time 970) (< time 1070)) '((stim2 nil)))
+    ((and (>= time 1070) (< time 2070)) 'NIL)
+    (t nil)))
+
+
+
+#| fitness values |#
+
+;; the RT data is only for correct responses.
+(defvar *fitness-accuracy* 0.8)   ; multiplier for f_a
+(defvar *fitness-time* 0.2)       ; multiplier for f_t
+(defvar *fitness-size* 0.0)       ; multiplier for f_s
+
+(defvar *data-rt-V-50* 376)           ; parameter used in computing f_t
+(defvar *data-rt-I-50* 399)       
+(defvar *data-acc-V-50* 0.9697)          ; parameter used in computing f_a
+(defvar *data-acc-I-50* 0.949)             
+
+(defvar *data-rt-V-68* 355)
+(defvar *data-rt-I-68* 394)  
+(defvar *data-acc-V-68* 0.9708)              
+(defvar *data-acc-I-68* 0.9568)          
+
+(defvar *data-rt-V-86* 349)
+(defvar *data-rt-I-86* 404) 
+(defvar *data-acc-V-86* 0.9623)          
+(defvar *data-acc-I-86* 0.9155)         
+
+(defstruct fitness-values
+  prob-val
+  rt-v
+  rt-i
+  acc-v
+  acc-i)
+
+(setf *fit-vals*
+      (list
+       (make-fitness-values :prob-val 50 :rt-v *data-rt-V-50* :rt-i *data-rt-I-50* :acc-v *data-acc-V-50* :acc-i *data-acc-I-50*)
+       (make-fitness-values :prob-val 68 :rt-v *data-rt-V-68* :rt-i *data-rt-I-68* :acc-v *data-acc-V-68* :acc-i *data-acc-I-68*)
+       (make-fitness-values :prob-val 86 :rt-v *data-rt-V-86* :rt-i *data-rt-I-86* :acc-v *data-acc-V-86* :acc-i *data-acc-I-86*)
+       ))
+
+#|
+### Run experiment ###
+|#
+
+(defun run-experiment (program) 
+  "Run a single experiment against the given program, returning information on performance"
+  (let* ((experiment-results '()))
+    (dolist (cue-prob *cue-probs*)
+      (let* ((cond-results '())
+             (current-stimuli (alexandria:shuffle (create-stimuli ntrials cue-prob))))
+        (setf s-a *strength-assoc*) ;; reset this between blocks
+        (setf current-prob cue-prob) ;; this is *current-cue-validity* in old code
+
+        (dolist (input-list current-stimuli)
+          (setf (input-direction input-1) (first input-list))
+          (setf (input-location input-2) (second input-list))
+          
+          (let* ((md (make-model :inputs (list input-1 input-2) :strength-assoc s-a :prev-trial pre-val)))
+
+            ;; update the timeline info so know where the second stim is
+            (setf (second (first (timeline (first *response-window*))))
+                  (input-location input-2))
+
+            (interpret program md)
+
+            (let* ((result (make-result :inputs input-list :validity (get-cue-validity input-list) :prob-valid cue-prob)))
+              
+              (when (AND (< (first *response-window*) (model-clock md)) (> (second *response-window*) (model-clock md))) ; when clock is before end time for response
+                (setf (result-response result) (model-response md)) ; record model's response
+                (setf (result-accuracy result) ; record accuracy
+                      (if (string= (result-response result) (second input-list)) 1 0))
+                (setf (result-timing result) (- (model-clock md) (first *response-window*))) ; record the response time
+                )
+              (push result cond-results)
+              (incf s-a (update-from-trial *ResWagrate* (model-strength-assoc md) (result-validity result)))
+              (setf pre-val (result-validity result))
+
+              )))
+        (push cond-results experiment-results)))
+    experiment-results))
+
+(defun run-gp (&key (max-generations 100)
+                    (population-size 1000)
+                    
+                    (f-a *fitness-accuracy*) ; proportion of accuracy objective to include in fitness
+                    (f-t *fitness-time*) ; proportion of response time objective
+                    (f-s *fitness-size*) ; proportion of program size objective
+                    
+                    ;;(rt *data-rt*) ; parameter for calculating response time objective
+                    ;;(acc *data-acc*) ; parameter for calculating accuracy objective
+                    (size 100) ; program size
+                    
+                    (logger nil) ; logger function
+                    (phased nil) ; set to t to use phased introduction
+                    ;;(fit-struct 1) ; default to fit all at the same time
+                    
+                    (mutation-rate 0.05)
+                    (i-depth 2) ;initial depth
+                    )
+  "Sets all relevant parameters for the GP system and model evaluation"
+  (setf
+   *max-generations* max-generations
+   *fitness-accuracy* f-a
+   *fitness-time* f-t
+   *fitness-size* f-s
+   ;;*data-rt* rt ;; these are set in *fit-vals*
+   ;;*data-acc* acc
+   *size-ps* size
+   *phased* phased
+   *phase* 1 ; no need for this to change
+   ;;*fit-struct* fit-struct
+   )
+
+  (gems:launch (operator-set) #'evaluate-fitness
+               :total-generations max-generations
+               :population-size population-size
+               :initial-depth i-depth
+               :maximum-depth 10
+               :elitism t
+               :type :steady-state
+               :mutation-rate mutation-rate ; mutation-rate. default is 0.05. is a probability.
+               :logger logger))

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 1110 - 0
published-results/ecvp-2023-posner/posner-initialise.lisp


+ 631 - 0
published-results/ecvp-2023-posner/posner-tests.lisp

@@ -0,0 +1,631 @@
+;; This file was created by Laura
+
+;; It will run a series of tests for the operators involved in the Posner experiment.
+;; THIS IS SPECIFICALLY FOR ARJONA
+
+;; code checking 
+(defun check (query error-message)
+  (if query
+      (format t ".")
+      (format t "~&Error: ~a~&" error-message)))
+
+;; need to load the relevant information in order to run the checks
+;; will just run the Chao experiment file for this - this includes all run-gp run-experiment and loads interpret file
+
+(require :asdf)
+(require :gems)
+(setf *default-pathname-defaults*
+      #P"/Users/bartletl/Documents/portacle/projects/gems-1.2-alpha-3/posner/")
+      ;;#P"/home/laurab/common-lisp/gems-1.2-alpha-3/posner/")
+
+(load "posner-initialise.lisp")
+(load "posner-ARJONA.lisp")
+
+;; TESTING JUST THE DECAY TOGGLE STUFF
+#|
+(setf decay-toggle 1) ;; just on for these tests
+(setf *decay-threshold* 0.6) ;; just try different values
+
+(defparameter stored-state (make-random-state t))
+(setf *random-state* (make-random-state stored-state)) ;; to load previous
+
+(defun test-decay ()
+  (setf *decay-threshold* 0.6) ;; just try different values
+  (let* ((md (make-model :current test-input-1 :salient test-input-2)))
+    (interpret '(prog3 (put-stm) (attend) (put-stm)) md)
+    (check (equal 2 (input-name (second (model-stm md)))) "test-input-1 is in stm2")
+    (check (equal 3 (input-name (first (model-stm md)))) "test-input-2 is in stm1")
+    (setf (model-clock md) 1500)
+    (interpret '(rehearsal-1) md)
+    (setf (model-clock md) 2000)
+    (interpret '(retrieve-1) md)
+    ;;(print md)
+    (check (equal nil (input-name (second (model-stm md)))) "test-input-1 has decayed")
+    (check (equal 3 (input-name (first (model-stm md)))) "test-input-2 is still good")
+    (setf *decay-threshold* 0.0)
+    (let* ((md (make-model :current test-input-1 :salient test-input-2)))
+      (interpret '(prog3 (put-stm) (attend) (put-stm)) md)
+      (check (equal 2 (input-name (second (model-stm md)))) "test-input-1 is in stm2")
+      (check (equal 3 (input-name (first (model-stm md)))) "test-input-2 is in stm1")
+      (setf (model-clock md) 1500)
+      (interpret '(rehearsal-1) md)
+      (setf (model-clock md) 2000)
+      (interpret '(retrieve-1) md)
+      ;;(print md)
+      (check (equal 2 (input-name (second (model-stm md)))) "test-input-1 has NOT decayed")
+      (check (equal 3 (input-name (first (model-stm md)))) "test-input-2 is still good")
+      (interpret '(prog2 (wait-1500) (retrieve-1)) md)
+      (check (equal 2 (input-name (second (model-stm md)))) "test-input-1 has NOT decayed - again")
+      (check (equal 3 (input-name (first (model-stm md)))) "test-input-2 is still good - again")
+      )))
+|#
+
+(setf decay-toggle 0) ;; just off for tests for now
+
+(defun test-nil-op ()
+  (let ((md (make-model :current test-input-1)))
+    (check (equal :centre (input-location (model-current md))) "initial current value")
+    (interpret '(nil) md)
+    (check (equal nil (input-location (model-current md))) "nil current location")
+    (check (equal nil (input-type (model-current md))) "nil current type")))
+;; WORKS: Aj
+
+(defun test-if-resp ()
+  (let ((md (make-model :salient test-input-2 :current test-input-1 :clock (first *response-window*))))
+    (interpret '(if (current-target-p) (respond-left) (respond-right)) md)
+    (check (equal (model-response md) :right)  "input-1 is cue, so false")
+    (interpret '(if (current-cue-p) (respond-left) (respond-right)) md)
+    (check (equal (model-response md) :left)  "input-1 is cue, so true")
+
+    (interpret '(attend) md)
+    (interpret '(if (current-cue-p) (respond-left) (respond-right)) md)
+    (check (equal (model-response md) :right)  "input-2 is target, so false")
+    (interpret '(if (current-target-p) (respond-left) (respond-right)) md)
+    (check (equal (model-response md) :left)  "input-2 is target, so true")))
+;; WORKS: Aj
+
+(defun test-prog ()
+  (let ((md (make-model)))
+    (check (equal 0 (model-clock md)) "initial clock 0")   
+    (interpret '(prog2 (wait-100) (wait-100)) md)
+    (check (equal 200 (model-clock md)) "updated clock 2x100")
+    (interpret '(prog3 (wait-100) (wait-100) (wait-100)) md)
+    (check (equal 500 (model-clock md)) "updated clock 3x100")
+    (interpret '(prog4 (wait-100) (wait-100) (wait-100) (wait-100)) md)
+    (check (equal 900 (model-clock md)) "updated clock 4x100")))
+;; WORKS: Aj
+
+(defun test-put-stm ()
+  (let ((md (make-model :current test-input-1 :salient test-input-2)))
+    (check (equal nil (input-location (first (model-stm md)))) "initial empty stm1")
+    (check (equal nil (input-location (second (model-stm md)))) "initial empty stm2")
+    (check (equal nil (input-location (third (model-stm md)))) "initial empty stm3")
+
+    (interpret '(put-stm) md)
+    (check (equal :centre (input-location (first (model-stm md)))) "fill stm1")
+    (check (equal nil (input-location (second (model-stm md)))) "second empty stm2")
+    (check (equal nil (input-location (third (model-stm md)))) "second empty stm3")
+
+    (interpret '(prog2 (attend) (put-stm)) md)
+    (check (equal :left (input-location (first (model-stm md)))) "update stm1")
+    (check (equal :centre (input-location (second (model-stm md)))) "move 1 to stm2")
+    (check (equal nil (input-location (third (model-stm md)))) "third empty stm3")
+    ;;(print md)
+
+    (interpret '(put-stm) md)
+    (check (equal :left (input-location (first (model-stm md)))) "update stm1 again")
+    (check (equal :left (input-location (second (model-stm md)))) "move 1 to stm2 again")
+    (check (equal :centre (input-location (third (model-stm md)))) "move 2 to stm3")
+
+    (interpret '(put-stm) md)
+    (check (equal :left (input-location (first (model-stm md)))) "update stm1 again again")
+    (check (equal :left (input-location (second (model-stm md)))) "move 1 to stm2 again again")
+    (check (equal :left (input-location (third (model-stm md)))) "remove the previous stm3")
+    ))
+;; WORKS: Aj
+
+(defun test-attend ()
+  (let ((md (make-model :salient test-input-1)))
+    (check (equal nil (input-location (model-current md))) "check current is empty")
+    (interpret '(attend) md)
+    (check (equal :centre (input-location (model-current md))) "current updated with salient")
+    ))
+;; WORKS: Aj
+
+(defun test-move-att ()
+  (let ((md (make-model :attfocus :left :current test-input-1)))
+    (check (equal :left (model-attfocus md)) "model start left")
+    (interpret '(move-att-left) md)
+    (check (equal 0 (model-clock md)) "nothing changed so no change to clock")
+    (interpret '(move-att-centre) md)
+    (check (equal :centre (model-attfocus md)) "model moved to centre")
+    (interpret '(move-att-centre) md)
+    (check (equal 70 (model-clock md)) "nothing changed so no change to clock")
+    (interpret '(move-att-left) md)
+    (check (equal :left (model-attfocus md)) "model moved to left")
+    (interpret '(move-att-right) md)
+    (check (equal :right (model-attfocus md)) "model moved to right")
+    (interpret '(move-att-right) md)
+    (check (equal 210 (model-clock md)) "nothing changed so no change to clock")
+    (interpret '(move-att-cue) md)
+    (check (equal :left (model-attfocus md)) "model moved to left, because cue direction")
+    (check (equal 350 (model-clock md)) "clock increased because operations")
+    (interpret '(move-att-cue) md)
+    (check (equal 420 (model-clock md)) "clock increased but only but cog for cue")
+    (let ((md (make-model :attfocus :right)))
+      (interpret '(move-att-cue) md)
+      (check (equal :right (model-attfocus md)) "no cue saved so no effect (except time)")
+      (check (equal 70 (model-clock md)) "time change")
+    )))
+;; WORKS: Aj
+
+(defun test-attn-capture-loc ()
+  (let ((md (make-model :attfocus :left :inputs (list test-input-1 test-input-2) :clock 310)))
+    (check (equal :left (model-attfocus md)) "model start left")
+    (interpret '(attn-capture-location) md)
+    ;;(print md)
+    (check (equal :centre (model-attfocus md)) "attn captured to centre")
+    (let ((md (make-model :clock (first *response-window*) :inputs (list test-input-1 test-input-2))))
+      (check (equal :centre (model-attfocus md)) "attnfocus is at default centre position")
+      ;;(setf test-mod md)
+      (interpret '(attn-capture-location) md)
+      (check (equal :left (model-attfocus md)) "attnfocus is left, in line with test-input-2")
+      (let ((md (make-model :inputs (list test-input-1 test-input-2) :attfocus :right))) ;; default clock, so no stim
+	(check (equal :right (model-attfocus md)) "attn right at start")
+	(check (equal 0 (model-clock md)) "clock default 0")
+	(interpret '(attn-capture-location) md)
+	(check (equal :right (model-attfocus md)) "attn still to right - no stim")))))
+;; WORKS: Arjona
+
+
+(defun test-detect ()
+  (let ((md (make-model :inputs (list test-input-1) :clock 300)))
+    (check (equal input-nil (model-salient md)) "salient is nil")
+    (interpret '(detect) md)
+    (check (equal test-input-1 (model-salient md)) "salient is now test-input 1"))
+  (let ((md (make-model :inputs (list test-input-1) :attfocus :left :clock 300)))
+    (check (equal input-nil (model-salient md)) "salient is nil")
+    (interpret '(detect) md)
+    (check (equal input-nil (model-salient md)) "salient is still nil because stim in centre")))
+;; WORKS: Aj
+
+(defun test-respond ()
+  (let ((md (make-model :current test-input-1)))
+    (check (equal nil (model-response md)) "response is nil")
+    (interpret '(respond-current) md)
+    (check (equal :centre (model-response md)) "responding with what is in current")
+    (interpret '(respond-left) md)
+    (check (equal :left (model-response md)) "responding left")
+    (interpret '(respond-right) md)
+    (check (equal :right (model-response md)) "responding right")
+    (interpret '(respond-cue) md)
+    (check (equal :left (model-response md)) "respond left, direction of cue")
+    (interpret '(respond-cue-opp) md)
+    (check (equal :right (model-response md)) "respond right, opposite direction of cue")
+    (interpret '(prog2 (nil) (respond-current)) md)
+    (check (equal :right (model-response md)) "still left because nothing in current")
+    ))
+;; WORKS: Aj
+
+(defun test-prev-cue ()
+  (let ((md (make-model :current test-input-1 :prev-trial "V")))
+    (interpret '(prev-val) md)
+    (check (equalp (model-response md) :left) "response in line with previous trial validity")
+    (let ((md (make-model :current test-input-1 :prev-trial "I")))
+      (interpret '(prev-val) md)
+      (check (equalp (model-response md) nil) "no change in response because no benefit from valid")
+      )))
+
+
+
+(defun test-rehearsal ()
+  (let ((md (make-model :current test-input-1 :salient test-input-2)))
+    (interpret '(prog2 (dotimes-2 (put-stm)) (prog2 (attend) (put-stm))) md)
+    (check (equal (input-location test-input-1) (input-location (second (model-stm md)))) "put item1 in stm2")
+    (check (equal (input-location test-input-1) (input-location (third (model-stm md)))) "put item1 in stm3")
+
+    (check (equal (input-location test-input-2) (input-location (first (model-stm md)))) "put item2 in stm1")
+
+    (check (not (equal (input-store-time (third (model-stm md))) (model-clock md))) "stm3 not same as clock")
+    (check (not (equal (input-store-time (second (model-stm md))) (model-clock md))) "stm2 not same as clock")
+    
+    (interpret '(rehearsal-2) md)
+    (check (equal (input-store-time (second (model-stm md))) (model-clock md)) "updated store time stm2")
+    
+    (interpret '(rehearsal-3) md)
+    (check (equal (input-store-time (third (model-stm md))) (model-clock md)) "updated store time stm3")
+
+    (check (not (equal (input-store-time (first (model-stm md))) (model-clock md))) "stm1 not same as clock")
+    (interpret '(rehearsal-1) md)
+    (check (equal (input-store-time (first (model-stm md))) (model-clock md)) "updated store time stm1")
+))
+;; WORKS: Aj
+
+(defun test-while ()
+  (let ((md (make-model :current test-input-1 :salient test-input-2)))
+    (interpret '(while-200 (put-stm)) md)
+    (check (equal (input-location input-1) (input-location (third (model-stm md)))) "repeated enough that input1 is in stm3")
+    (interpret '(prog2 (attend) (while-100 (put-stm))) md)
+    (check (equal (input-location test-input-1) (input-location (third (model-stm md)))) "input1 is still in stm3")
+    (check (equal (input-location test-input-2) (input-location (second (model-stm md)))) "input2 is in stm2")
+    (check (equal (input-location test-input-2) (input-location (first (model-stm md)))) "input2 is in stm1")
+    ))
+;; WORKS: Aj
+
+(defun test-wait ()
+  (let ((md (make-model)))
+    (check (equal 0 (model-clock md)) "model-clock at 0")
+    (interpret '(wait-25) md)
+    (check (equal 25 (model-clock md)) "model-clock at 25")
+    (interpret '(wait-50) md)
+    (check (equal 75 (model-clock md)) "model-clock at 75")
+    (interpret '(wait-100) md)
+    (check (equal 175 (model-clock md)) "model-clock at 175")
+    (interpret '(wait-200) md)
+    (check (equal 375 (model-clock md)) "model-clock at 375")
+    (interpret '(wait-1000) md)
+    (check (equal 1375 (model-clock md)) "model-clock at 1375")
+    (interpret '(wait-1500) md)
+    (check (equal 2875 (model-clock md)) "model-clock at 2875")
+    (interpret '(wait-.5trial) md)
+    (check (equal (+ 2875 (/ (second *response-window*) 2)) (model-clock md)) "model-clock added .5 of trial")
+    (setf (model-clock md) 0)
+    (interpret '(wait-.25trial) md)
+    (check (equal (/ (second *response-window*) 4) (model-clock md)) "model-clock added .25 of trial")
+    (setf (model-clock md) 0)
+    (interpret '(wait-.1trial) md)
+    (check (equal (/ (second *response-window*) 10) (model-clock md)) "model-clock added .25 of trial")
+    ;;(print md)
+    ))
+;; WORKS: Aj
+
+(defun test-dotimes ()
+  (let ((md (make-model)))
+    (check (equal 0 (model-clock md)) "model-clock at 0")
+    (interpret '(dotimes-2 (wait-100)) md)
+    (check (equal 200 (model-clock md)) "model-clock at 200")
+    (interpret '(dotimes-3 (wait-100)) md)
+    (check (equal 500 (model-clock md)) "model-clock at 500")
+    (interpret '(dotimes-4 (wait-100)) md)
+    (check (equal 900 (model-clock md)) "model-clock at 900")
+    ))
+;; WORKS: Aj
+
+(defun avg (faff) (/ (apply '+ faff) (length faff)))
+
+(defun test-match-prob ()
+  (setf tally-86 nil)
+  (setf tally-50 nil)
+  (setf tally-68 nil)
+  (dotimes (more 50)
+    (setf current-prob 0.86)
+    (setf tally 0)
+    (let* ((md (make-model :current test-input-1)))
+      (check (equal :left (input-direction (model-current md))) "check input direction is left")
+      (dotimes (rep 100)
+	(interpret '(match-probability) md)
+	(when (equalp :left (model-response md)) (setf tally (1+ tally))))   
+      ;;(check (< 66 tally) "is it in line with response?")
+      (push tally tally-86) 
+    
+    (let* ((md (make-model :current test-input-1)))
+      (setf current-prob 0.5)
+      (setf tally 0)
+      (dotimes (rep 100)
+	(interpret '(match-probability) md)
+	(when (equalp :left (model-response md)) (setf tally (1+ tally))))
+      ;;(check (AND (< 30 tally) (> 70 tally)) "is it in line with response?")
+      (push tally tally-50) 
+
+      (let* ((md (make-model :current test-input-1)))
+	(setf current-prob 0.68)
+	(setf tally 0)
+	(dotimes (rep 100)
+	  (interpret '(match-probability) md)
+	  (when (equalp :left (model-response md)) (setf tally (1+ tally))))
+	;;(check (AND (< tally 88) (> tally 48)) "is it in line with response?")
+	(push tally tally-68)
+	))))
+  (print tally-86)
+  (print (avg tally-86))
+  (check (< 66 (avg tally-86)) "tally-86 avg")
+  (print tally-50)
+  (print (avg tally-50))
+  (check (AND (< 30 (avg tally-50)) (> 70 (avg tally-50))) "tally-50 avg")
+  (print tally-68)
+  (print (avg tally-68))
+  (check (AND (< (avg tally-68) 88) (> (avg tally-68) 48)) "tally-68 avg")
+  )
+;; WORKS: Aj      
+
+(defun test-RW-cue-strength () ;; so it takes the *strength-assoc* and works out how likely the cue is. In run-experiment, the *strength-assoc* is updated based on the validity of the trial. so can just run with a variety of strength-assoc's and see whether it outputs in line with that.
+
+  (setf tally-50 nil)
+  (setf tally-75 nil)
+  (setf tally-25 nil)
+
+  (dotimes (more 10)
+
+    (setf tally 0)
+    (dotimes (we 100)
+      (let ((md (make-model :strength-assoc 0.5 :current test-input-1)))
+        (interpret '(RW-cue-strength) md)
+        (when (equalp :left (model-response md)) (setf tally (1+ tally)))))
+    (push tally tally-50)
+
+    (setf tally 0)
+    (dotimes (we 100)
+      (let ((md (make-model :strength-assoc 0.75 :current test-input-1)))
+        (interpret '(RW-cue-strength) md)
+        (when (equalp :left (model-response md)) (setf tally (1+ tally)))))
+    (push tally tally-75)
+
+    (setf tally 0)
+    (dotimes (we 100)
+      (let ((md (make-model :strength-assoc 0.25 :current test-input-1)))
+        (interpret '(RW-cue-strength) md)
+        (when (equalp :left (model-response md)) (setf tally (1+ tally)))))
+    (push tally tally-25))
+
+  (print tally-75)
+  (print (avg tally-75))
+  (check (AND (< 55 (avg tally-75)) (> 95 (avg tally-75))) "tally-75 avg")
+  (print tally-50)
+  (print (avg tally-50))
+  (check (AND (< 30 (avg tally-50)) (> 70 (avg tally-50))) "tally-50 avg")
+  (print tally-25)
+  (print (avg tally-25))
+  (check (AND (< (avg tally-25) 45) (> (avg tally-25) 5)) "tally-25 avg")
+  )
+;; WORKS: Aj (still need to test the updating of the strength-assoc)
+
+(defun test-RW-cue-percept ()
+  ;; uses the cue and perception of stimulus (not strength of learning?)
+  ;; still uses the strength-assoc, but it only slighty affects things.
+  ;; RW-predict-stim (strength-assoc, cue-direction)
+
+  (setf tally-50-match nil)
+  (setf tally-90-match nil)
+  (setf tally-10-match nil)
+  (setf tally-50-no nil)
+  (setf tally-90-no nil)
+  (setf tally-10-no nil)
+
+  (let* ((md (make-model :strength-assoc 0.5 :current test-input-1)))
+    (interpret '(RW-cue-percept) md)
+    (check (equal nil (model-response md)) "don't have the target so shouldnt do anything")
+
+
+      (dotimes (faff 100)
+
+        (setf tally 0)
+        (dotimes (we 100)
+          (let* ((md (make-model :strength-assoc 0.5 :current test-input-2 :stm (list test-input-1 input-nil input-nil))))
+
+          (interpret '(rw-cue-percept) md)
+          (when (equalp :left (model-response md)) (setf tally (1+ tally)))))
+        (push tally tally-50-match)
+
+        ;;(setf (model-strength-assoc md) 0.90)
+        (setf tally 0)
+        (dotimes (we 100)
+          (let* ((md (make-model :strength-assoc 0.9 :current test-input-2 :stm (list test-input-1 input-nil input-nil))))
+
+          (interpret '(rw-cue-percept) md)
+          (when (equal :left (model-response md)) (setf tally (1+ tally)))))
+        (push tally tally-90-match)
+        
+        ;;(setf (model-strength-assoc md) 0.10)
+        (setf tally 0)
+        (dotimes (we 100)
+          (let* ((md (make-model :strength-assoc 0.1 :current test-input-2 :stm (list test-input-1 input-nil input-nil))))
+          (interpret '(rw-cue-percept) md)
+          (when (equal :left (model-response md)) (setf tally (1+ tally)))))
+        (push tally tally-10-match)
+        )
+    
+
+      ;;(interpret '(attend) md)
+      ;;(print md)
+      ;;(check (equal (input-location test-input-3) (model-response md)) "current is now not matched with cue")
+
+      (dotimes (faff 100)
+        (setf tally 0)
+        (dotimes (we 100)
+          (let* ((md (make-model :strength-assoc 0.5 :current test-input-3 :stm (list test-input-1 input-nil input-nil))))
+
+          (interpret '(rw-cue-percept) md)
+          (when (equal :left (model-response md)) (setf tally (1+ tally)))))
+        (push tally tally-50-no)
+
+        ;;(setf (model-strength-assoc md) 0.90)
+        (setf tally 0)
+        (dotimes (we 100)
+          (let* ((md (make-model :strength-assoc 0.9 :current test-input-3 :stm (list test-input-1 input-nil input-nil))))
+
+          (interpret '(rw-cue-percept) md)
+          (when (equal :left (model-response md)) (setf tally (1+ tally)))))
+        (push tally tally-90-no)
+        
+        (setf (model-strength-assoc md) 0.10)
+        (setf tally 0)
+        (dotimes (we 100)
+          (let* ((md (make-model :strength-assoc 0.1 :current test-input-3 :stm (list test-input-1 input-nil input-nil))))
+
+          (interpret '(rw-cue-percept) md)
+          (when (equal :left (model-response md)) (setf tally (1+ tally)))))
+        (push tally tally-10-no)
+        )
+
+      (print tally-50-match)
+      (print (list (avg tally-50-match) 0.975))
+      ;;(check (AND (< 55 (avg tally-50-match)) (> 95 (avg tally-50-match))) "tally-50-match avg") ;;0.975
+
+      (print tally-50-no)
+      (print (list (avg tally-50-no) 0.925))
+      ;;(check (AND (< 55 (avg tally-50-no)) (> 95 (avg tally-50-no))) "tally-50-no avg")
+
+      (print tally-90-match)
+      (print (list (avg tally-90-match) 0.995))
+      ;;(check (AND (< 55 (avg tally-90-match)) (> 95 (avg tally-90-match))) "tally-90-match avg") ;;0.995
+
+      (print tally-90-no)
+      (print (list (avg tally-90-no) 0.905))
+      ;;(check (AND (< 55 (avg tally-90-no)) (> 95 (avg tally-90-no))) "tally-90-no avg") ;; 0.905
+
+      (print tally-10-match)
+      (print (list (avg tally-10-match) 0.955))
+      ;;(check (AND (< 55 (avg tally-10-match)) (> 95 (avg tally-10-match))) "tally-10-match avg") ;;0.955
+
+      (print tally-90-no)
+      (print (list (avg tally-10-no) 0.945))
+      ;;(check (AND (< 55 (avg tally-10-no)) (> 95 (avg tally-10-no))) "tally-10-no avg") ;; 0.945
+
+      )
+  )
+;; WORKS: Aj (requires manual checking of output because i'm lazy)
+  
+          
+(defun test-if-sa ()
+  (let* ((md (make-model :current test-input-1 :strength-assoc 0.5)))
+    (interpret '(if-strength-assoc) md)
+    (check (equal nil (model-response md)) "no change because not at threshold")
+    (let* ((md (make-model :current test-input-1 :strength-assoc 0.71)))
+      (interpret '(if-strength-assoc) md)
+      (check (equal :left (model-response md)) "changed in line with cue"))))
+;; WORKS: Aj
+      
+
+    
+
+
+(defun test-current-type ()
+  (let ((md (make-model :salient test-input-2 :current test-input-1)))
+    (interpret '(if (current-target-p) (respond-left) (respond-right)) md)
+    (check (equal :right (model-response md)) "check right response as not target")
+    (interpret '(if (current-cue-p) (respond-left) (respond-right)) md)
+    (check (equal :left (model-response md)) "check left response as is cue")
+    (interpret '(prog2 (attend) (if (current-target-p) (respond-left) (respond-right))) md)
+    (check (equal :left (model-response md)) "check left response as is target")
+    (interpret '(if (current-cue-p) (respond-left) (respond-right)) md)
+    (check (equal :right (model-response md)) "check right response as not cue")
+    ))
+;; works: Aj
+
+(defun test-retrieve ()
+  (let ((md (make-model :salient test-input-2 :current test-input-1)))
+    (interpret '(prog3 (put-stm) (prog2 (attend) (put-stm)) (put-stm)) md)
+
+    (check (equal (input-location test-input-1) (input-location (third (model-stm md)))) "stm3 is item1")
+    (check (equal (input-location test-input-2) (input-location (second (model-stm md)))) "stm2 is item2")
+    (check (equal (input-location test-input-2) (input-location (first (model-stm md)))) "stm1 is item2")
+    (check (equal (input-location test-input-2) (input-location (model-current md))) "model current is item2")
+    
+    (interpret '(retrieve-3) md)
+    (check (equal (input-location test-input-1) (input-location (model-current md))) "model current is now item1, which was in stm3")
+    (interpret '(retrieve-2) md)
+    (check (equal (input-location test-input-2) (input-location (model-current md))) "model current is now item2, which was in stm2")
+    (interpret '(retrieve-3) md)
+    (check (equal (input-location test-input-1) (input-location (model-current md))) "model current is now item1, which was in stm3")
+    (interpret '(retrieve-1) md)
+    (check (equal (input-location test-input-2) (input-location (model-current md))) "model current is now item2, which was in stm1")
+
+    (interpret '(retrieve-target-stm) md)
+    (check (equal (input-type test-input-2) (input-type (model-current md))) "model current is now the target item")
+    (interpret '(retrieve-cue-stm) md)
+    (check (equal (input-type test-input-1) (input-type (model-current md))) "model current is now the most recent stimulus item")
+    ))
+;; WORKS: Aj
+
+(defun test-shift-attn ()
+  (let ((md (make-model :attfocus :centre)))
+    (check (equal (model-attfocus md) :centre)  "attention focus is centre")
+    (interpret '(shift-attn-cw) md)
+    (check (equal (model-attfocus md) :right)  "attention focus is right")
+    (interpret '(shift-attn-cw) md)
+    (check (equal (model-attfocus md) :left)  "attention focus is left (all the way round)")
+    (interpret '(shift-attn-cw) md)
+    (check (equal (model-attfocus md) :centre)  "attention focus is centre (from left)")
+    (interpret '(shift-attn-ccw) md)
+    (check (equal (model-attfocus md) :left)  "attention focus is left")
+    (interpret '(shift-attn-ccw) md)
+    (check (equal (model-attfocus md) :right)  "attention focus is right (from all the way round)")
+    (interpret '(shift-attn-ccw) md)
+    (check (equal (model-attfocus md) :centre)  "attention focus is centre (from right)")
+    ))
+;; WORKS: Aj
+  
+
+
+
+;; timings have changed, so that ps have to give a response within time to save the results, rather than in the operator code... maybe change this though?
+(defun test-dmts-ops ()
+  (let ((md (make-model :inputs (list input-1 input-2 input-3))))
+    (check (equal input-nil (model-current md)) "current starts with input-nil")
+    (interpret '(detect-attend) md)
+    (check (equal input-1 (model-current md)) "current changes to input1")
+    (interpret '(prog2 (move-att-left) (detect-attend)) md)
+    (check (equal input-nil (model-current md)) "current changes to nil, due to clock")
+    (setf (model-clock md) 1600)
+    (interpret '(detect-attend) md)
+    (check (equal input-2 (model-current md)) "current changes to input2")
+    (interpret '(prog2 (move-att-right) (detect-attend)) md)
+    (check (equal input-3 (model-current md)) "current changes to 3")
+    (check (equal nil (model-response md)) "default response is nil")
+    (setf (model-clock md) (- (first *response-window*) 200))
+    (interpret '(respond-left) md)
+    (check (equal nil (model-response md)) "no response, due to clock (too early)")
+    (setf (model-clock md) (first *response-window*))
+    (interpret '(respond-right) md)
+    (check (equal :right (model-response md)) "responds right")
+    (interpret '(respond-left) md)
+    (check (equal :left (model-response md)) "responds left")
+    (interpret '(respond-current) md)
+    (check (equal :right (model-response md)) "responds current - input3 (right)")
+    ))
+
+(defun test-program-depth ()
+  (check (= 0 (program-depth '())) "empty tree")
+  (check (= 1 (program-depth '(x))) "single entry")
+  (check (= 2 (program-depth '(x (a) (b)))) "three items, two levels")
+  (check (= 1 (program-depth '(x a b))) "three items, one level, terminals")
+  (check (= 3 (program-depth '(x (a) (b (c))))) "four items, three levels")
+  )
+
+(defun test-program-size ()
+  (check (= 0 (program-size '())) "empty tree")
+  (check (= 1 (program-size '(x))) "single entry")
+  (check (= 3 (program-size '(x (a) (b)))) "three items, one level")
+  (check (= 3 (program-size '(x a b))) "three items, one level, terminals")
+  (check (= 4 (program-size '(x (a) (b (c))))) "four items, two levels")
+  )
+
+(defun run-tests ()
+  (format t "~&Testing: ")
+  (test-nil-op)
+  (test-if)
+  (test-prog)
+  (test-put-stm)
+  (test-attend)
+  (test-move-att)
+  (test-attn-capture)
+  (test-detect)
+  (test-respond)
+  (test-rehearsal)
+  (test-while)
+  (test-wait)
+  (test-dotimes)
+  (test-compare-current1-Rc)
+  (test-compare-current1-r1)
+  (test-compare-1-2-p)
+  (test-compare-current1-p)
+  (test-magic-operator)
+  (test-retrieve)
+  (test-dmts-ops)
+  ;;(test-program-depth)
+  ;;(test-program-size)
+  (format t "~%-------- Done~&"))
+
+
+
+

+ 7 - 0
published-results/sgai-2023-heuristics/README.md

@@ -0,0 +1,7 @@
+# SGAI 2023: Heuristic Search of Heuristics
+
+This folder contains the code for the experimental results 
+reported in:
+
+* A. Pirrone, P.C.R. Lane, L.K. Bartlett, N.Javed and F. Gobet, 'Heuristic search of heuristics', to appear in M.Bramer and F.Stahl (Eds.) _Artificial Intelligence XL. SGAI 2023. Lecture Notes in Computer Science(), vol 14381_, pp.407-420, 2023. (Springer, Cham.) https://doi.org/10.1007/978-3-031-47994-6_36
+

+ 600 - 0
published-results/sgai-2023-heuristics/with_noise/task_with_noise.lisp

@@ -0,0 +1,600 @@
+(require :asdf)
+(require :gems)
+(require :lhstats)
+
+(defparameter $inputT 100)    ;; perception + attend
+(defparameter $outputT 140)   ;; intend + movement
+(defparameter $cogT 70)       ;; basic cognitive process
+(defparameter $stmT 50)       ;; basic STM process
+(defparameter $syntaxT 0)     ;; prog2, if      ;; these are different from cognitive operators
+
+(defparameter stored-state (make-random-state t))
+(setf *random-state* stored-state)
+
+;(print stored-state)
+ 
+
+(defvar *max-generations* 1000)
+(defvar *population-size* 1000)
+
+(defvar *fitness-rt_low* 0.35)   
+(defvar *fitness-rt_high* 0.35)       
+(defvar *fitness-sd_low* 0)  
+(defvar *fitness-sd_high* 0)      
+(defvar *fitness-acc_low* 0.3)       
+(defvar *fitness-acc_high* 0)   
+(defvar *propn-fitness-size* 0)     
+
+(defvar *time-rt* 714)            
+(defvar *rtlow* 784)            
+(defvar *rthigh* 704)           
+(defvar *sdlow* 294)            
+(defvar *sdhigh* 242)          
+(defvar *acclow* 1)         
+(defvar *acchigh* 1)          
+(defvar *size-ps* 100)          
+
+
+(defstruct model
+  "Defines the state of a model and its interactions with the environment"
+  clock     ; core model   
+  current
+  stm
+  (attFocus :centre) ;; focus of attention; default is centre
+  salient   ;; salient is what is salient in the visual field.
+  response  
+  inputs    ;; buffers for inputs
+  _screenLeft
+  _screenCentre
+  _screenRight
+  ( _inputName  "_")
+  ( _type :stimulus)
+  )
+  
+(defstruct input
+  (name nil)
+  (location :central)
+  (type :stimulus))
+  
+(defstruct result inputs timing response rt_low rt_high acc_low acc_high)
+
+(defun detect-main (md)
+  (let ((current-stimuli (timeline (model-clock md)))   
+        (att (model-attFocus md))
+        (stim nil))
+    (setf stim (first (find-if #'(lambda (y)
+                                   (eq (second y) att))
+                               current-stimuli)))
+    (setf (model-_inputname md) (subseq (symbol-name stim) 0 3))
+    (case att
+      (left   (setf (model-_screenLeft   md) stim))
+      (centre (setf (model-_screenCentre md) stim))
+      (right  (setf (model-_screenRight  md) stim)))))
+      
+(defun timeline (time)
+  (cond
+    ((and (>= time 0) (< time 10000)) '(stim1 centre))
+    (t nil)))
+;; These convenience functions allow interpret/display-pseudocode to
+;; work with both s-expressions and syntax-tree:node structures.
+(defun operator-label (operator)
+  "Returns label of given operator"
+  (typecase operator
+    (list 
+     (intern (symbol-name (first operator)) "KEYWORD"))
+    (syntax-tree:node
+     (intern (symbol-name (syntax-tree:node-label operator)) "KEYWORD"))
+    (otherwise
+     (error "Invalid operator type"))))
+     
+(defun operator-children (operator)
+  "Returns children of given operator"
+  (typecase operator
+    (list 
+     (rest operator))
+    (syntax-tree:node
+     (syntax-tree:node-children operator))
+    (otherwise
+     (error "Invalid operator type"))))
+;; interpret function evaluates the given operator (program), in the context of
+;; md, which is a model+environment definition
+(defun interpret (operator md)                                                
+  (when (syntax-tree:node-p operator) (incf (syntax-tree:node-entries operator))) 
+  (unless  (> (model-clock md) 10000) 
+    (case (operator-label operator)
+    
+      (:put-stm
+       (incf (model-clock md) $cogT)
+       (setf (model-stm md) 
+             (if (= 3 (length (model-stm md)))
+                 (list (model-current md)
+                       (first (model-stm md))
+                       (second (model-stm md)))
+                 (cons (model-current md)
+                       (model-stm md)))))
+                       
+      (:detect
+       (incf (model-clock md) $inputT)
+       (setf (model-salient md) (model-inputs md)))
+       
+      (:sample
+       (incf (model-clock md) $inputT)
+           (setf (model-current md) (model-salient md))
+           (if (< (random 1.0) .75)
+           (setf (model-current md) (model-salient md))
+           (if   (eq (model-salient md) 1); 0 is false, other numbers true
+             (setf (model-current md) 2)
+             (setf (model-current md) 1))
+             )         
+             )
+           
+           
+       
+       
+     
+       
+      (:respond
+       (incf (model-clock md) $outputT)
+       (setf (model-response md)
+             (if (< (random 1.0) 1)  (model-current md)  0)))
+
+      (:conservative
+       (incf (model-clock md) $outputT)
+       (setf (model-response md)
+       (cond ( (> (model-current md) 3) 
+                 1)
+          		((< (model-current md) -3)
+                 2)
+          		((and (< (model-current md) 3)  (> (model-current md) -3) ) 
+                   0)
+
+             )))   
+
+
+      (:threshold
+       (incf (model-clock md) $outputT)
+       (setf (model-response md)
+       (cond ( (> (model-current md) 1.2) 
+                 1)
+          		((<= (model-current md) 1.2)
+                 0)
+
+             )))   
+
+            (:respond_low
+       (incf (model-clock md) $outputT)
+       (setf (model-response md)
+             (if (< (model-current md) -3)  2  0)))      
+             
+      (:respondrandom
+       (incf (model-clock md) $outputT)
+       (setf (model-response md)
+             (if (< (random 1.0) .5) 1  2)))
+             
+      (:respond6
+       (incf (model-clock md) $outputT)
+       (setf (model-response md)
+             (if (< (random 1.0) .6)  (model-current md)  0)))
+             
+      (:respond7
+       (incf (model-clock md) $outputT)
+       (setf (model-response md)
+             (if (< (random 1.0) .7)  (model-current md)  0)))
+             
+      (:respond8
+       (incf (model-clock md) $outputT)
+       (setf (model-response md)
+             (if (< (random 1.0) .8)  (model-current md)  0)))
+      (:respond9
+       (incf (model-clock md) $outputT)
+       (setf (model-response md)
+             (if (< (random 1.0) .9)  (model-current md)  0)))
+             
+      (:ifM1
+       (incf (model-clock md) $syntaxT)
+       (if  (eq 1 (model-current md)) ; 0 is false, other numbers true
+            (interpret (first (operator-children operator)) md)
+            ))
+            
+      (:ifM2
+       (incf (model-clock md) $syntaxT)
+       (if  (eq 2 (model-current md)) ; 0 is false, other numbers true
+            (interpret (first (operator-children operator)) md)
+            ))
+            
+      (:if-mc
+       (incf (model-clock md) $syntaxT)
+       
+        (if  (eq 1 (model-current md)) ; 0 is false, other numbers true
+            (interpret (first (operator-children operator)) md)
+            (interpret (second (operator-children operator)) md)
+            )
+       
+       )
+      
+      (:ifosidf
+        (incf (model-clock md) $syntaxT)
+        (if (interpret (first (operator-children operator)) md)
+            (interpret (second (operator-children operator)) md)
+            (interpret (third (operator-children operator)) md)))
+            
+      (:prog2
+          (incf (model-clock md) $syntaxT)
+          (interpret (first (operator-children operator)) md)
+        (interpret (second (operator-children operator)) md))
+        
+      (:donothing
+       (incf (model-clock md) 0))
+       
+      (:wait-25
+       (incf (model-clock md) 25))
+      
+      (:wait-50
+       (incf (model-clock md) 50))
+      
+      (:wait-100
+       (incf (model-clock md) 100))
+      
+      (:wait-200
+       (incf (model-clock md) 200))
+      
+      (:wait-1000
+       (incf (model-clock md) 1000))
+      
+      (:wait-1500
+       (incf (model-clock md) 1500))
+       
+      (:prog3
+       (incf (model-clock md) $syntaxT)
+       (interpret (first (operator-children operator)) md)
+       (interpret (second (operator-children operator)) md)
+       (interpret (third (operator-children operator)) md))
+       
+      (:prog4
+       (incf (model-clock md) $syntaxT)
+       (interpret (first (operator-children operator)) md)
+       (interpret (second (operator-children operator)) md)
+       (interpret (third (operator-children operator)) md)
+       (interpret (fourth (operator-children operator)) md))
+       
+      (:dotimes-2
+       (incf (model-clock md) $syntaxT)
+       (dotimes (i 2)
+         (interpret (first (operator-children operator)) md)))
+      
+      (:dotimes-3 
+       (incf (model-clock md) $syntaxT)
+       (dotimes (i 3)
+         (interpret (first (operator-children operator)) md)))
+      
+      (:dotimes-5 
+       (incf (model-clock md) $syntaxT)
+       (dotimes (i 5)
+         (interpret (first (operator-children operator)) md)))
+         
+      (:dotimes-10
+       (incf (model-clock md) $syntaxT)
+       (dotimes (i 10)
+         (interpret (first (operator-children operator)) md)))
+         
+      (:while-50
+       (incf (model-clock md) $syntaxT)
+       (let ((start-time (model-clock md)))
+         (do ((x 1 (incf x)))  ;; as a safeguard...
+             ((or (>= (model-clock md) (+ start-time 50))
+                  (> x 4))) ;; as a safeguard...
+           (interpret (first (operator-children operator)) md))))
+      
+      (:while-100 
+       (incf (model-clock md) $syntaxT)
+       (let ((start-time (model-clock md)))
+         (do ((x 1 (incf x)))  ;; as a safeguard...
+             ((or (>= (model-clock md) (+ start-time 100))
+                  (> x 4))) ;; as a safeguard...
+           (interpret (first (operator-children operator)) md))))
+      
+      (:while-200 
+       (incf (model-clock md) $syntaxT)
+       (let ((start-time (model-clock md)))
+         (do ((x 1 (incf x)))  ;; as a safeguard...
+             ((or (>= (model-clock md) (+ start-time 200))
+                  (> x 5))) ;; as a safeguard...
+           (interpret (first (operator-children operator)) md))))
+      
+      
+      (:access-stm-1 
+       (incf (model-clock md) $stmT)
+       (setf (model-current md) (first (model-stm md))))
+       
+      (:access-stm-2
+       (incf (model-clock md) $stmT)
+       (setf (model-current md) (second (model-stm md))))
+       
+      (:access-stm-3
+       (incf (model-clock md) $stmT)
+       (setf (model-current md) (third (model-stm md))))
+       
+      (:compare-1-2
+       (incf (model-clock md) $cogT)
+       (setf (model-current md)
+             (if (= (first (model-stm md)) (second (model-stm md))) (first (model-stm md))  (model-current md))))
+             
+      (:compare-2-3
+       (incf (model-clock md) $cogT)
+       (setf (model-current md)
+             (if (= (second (model-stm md)) (third (model-stm md))) (second (model-stm md))  (model-current md))))
+             
+      (:compare-1-3
+       (incf (model-clock md) $cogT)
+       (setf (model-current md)
+             (if (= (first (model-stm md)) (third (model-stm md))) (first (model-stm md))  (model-current md))))
+
+
+    (:sum-1-2
+       (incf (model-clock md) $cogT)
+       (setf (model-current md)
+             (+ (first (model-stm md))  (second (model-stm md))))
+             
+             
+             )
+             
+(:boost
+       (incf (model-clock md) $cogT)
+       (setf (model-current md)
+             (+ (model-current md)  1))
+          
+             )
+(:decay
+       (incf (model-clock md) $cogT)
+       (setf (model-current md)
+             (* (model-current md)  0))
+             
+            
+             )
+      (:sum-1-3
+       (incf (model-clock md) $cogT)
+       (setf (model-current md)
+             (+ (first (model-stm md))  (third (model-stm md))))
+            )
+
+              (:sum-2-3
+       (incf (model-clock md) $cogT)
+       (setf (model-current md)
+             (+ (second (model-stm md))  (third (model-stm md))))
+            )
+
+     (:if
+       (incf (model-clock md) $syntaxT)
+       (if (> (model-current md) 1.2)
+       (interpret (first (operator-children operator)) md)
+       (interpret (second (operator-children operator)) md)))
+
+
+
+      
+      (otherwise ; error if comes across an unknown operator
+       (error "interpret: unknown operator ~a" (operator-label operator))))))
+;; operator set (name . number-of-children)
+;; TIP: comment out individual lines to ignore specific operators
+(defun operator-set () 
+  '(
+    (ACCESS-STM-1 . 0)
+    (ACCESS-STM-2 . 0)
+    (ACCESS-STM-3 . 0)
+    ;(COMPARE-1TO3 . 0)
+    (COMPARE-1-2 . 0)
+    (COMPARE-1-3 . 0)
+    (COMPARE-2-3 . 0)
+    (SUM-1-2 . 0)
+    ;(SUM-1-3 . 0)
+    ;(SUM-2-3 . 0)
+    ;(DETECT . 0)
+    ;(WHILE-50 . 1)
+    ;(WHILE-100 . 1)
+    ;(WHILE-200 . 1)
+    (PUT-STM . 0)
+    (SAMPLE . 0)
+    ;(BOOST . 0)
+    ;(DECAY . 0)
+    ;(SAMPLE4 . 0)
+    (THRESHOLD . 0)
+    ;(CONSERVATIVE . 0)
+    ;(RESPONDRANDOM . 0)
+    ;(RESPOND6 . 0)
+    ;(RESPOND7 . 0)
+    ;(RESPOND8 . 0)
+    ;(RESPOND9 . 0)
+    (WAIT-50 . 0)
+    (WAIT-25 . 0)
+    (WAIT-100 . 0)
+    (WAIT-200 . 0)
+    (WAIT-1000 . 0)
+    (WAIT-1500 . 0)
+    (DONOTHING . 0)
+    (DOTIMES-2 . 1)
+    (DOTIMES-3 . 1)
+    (DOTIMES-5 . 1)
+    (DOTIMES-10 . 1)
+    (PROG2 . 2)
+    (PROG3 . 3)
+    (PROG4 . 4)
+    (IF . 2)
+    ;(IF_NC . 2)
+    ;(IF_CURRENT_TH . 3)
+    ;(IF_CURRENT_CON . 3)  
+    ))
+;; 1 is high magnitude, 2 is low magnitude
+
+(defparameter stimuli '(
+                         .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 
+                        .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 
+                        .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 
+                        .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 
+                        .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 
+                        .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 
+                        .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 
+                        .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 
+                        .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 
+                        .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 
+                        .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 
+                        .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 
+                        .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 
+                        .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 
+                        .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 
+                        
+                        ))
+;; -- run-experiment template
+;;    Complete the run-experiment code. 
+;; -- output: The return value(s) is used by 'evaluate-fitness' below.
+
+(defun run-experiment (program) 
+  (let ((results '())
+        (expt-stimuli (statistics:random-sample (length stimuli) stimuli)))
+    (dolist (input expt-stimuli)
+      ;; -- begin single trial
+      (let ((md (make-model :clock 0 :current 0 :stm '(0 0 0)
+                            :salient input :inputs input :response 0)))
+        (interpret program md)
+        ;; -- collect information and create result
+        (let ((result (make-result :timing 0 :inputs input :response 0 :rt_low 0 :rt_high 0 :acc_low 0 :acc_high 0)))
+          (setf (result-response result) (model-response md))           ; record model's response
+                    (setf (result-acc_low result) (model-response md))           ; record model's response
+
+          (if (eq (model-inputs md) .6)
+              (setf (result-rt_low result) (model-clock md) (result-rt_high result) -1000)
+              (setf (result-rt_high result) (model-clock md) (result-rt_low result) -1000))
+          (push result results))))
+    results))
+    
+(defun filter-non-negative (l)
+  (cond
+    ((null l)                   ;; empty list
+     nil)
+    ((>= (first l) 0)      ;; number >= 0
+     (cons (first l) (filter-non-negative (rest l))))
+    (t                         ;; all other cases 
+     (filter-non-negative (rest l)))))
+     
+(defun fitness-acc_low (acc_low)
+  (abs (- *acclow* acc_low)))
+(defun fitness-acc_high (acc_high)
+  (abs (- *acchigh* acc_high)))
+
+;; Computes the f_s objective function.
+(defun fitness-size (program-size)
+  (gems:half-sigmoid (/ program-size *size-ps*)))
+
+(defun fitness-sd_low (sd_low)
+  (gems:half-sigmoid (/ (abs (- sd_low *sdlow*))
+                        *time-rt*)))
+  
+(defun fitness-sd_high (sd_high)
+  (gems:half-sigmoid (/ (abs (- sd_high *sdhigh*))
+                        *time-rt*)))
+  
+(defun fitness-rt_low (rt_low)
+  (gems:half-sigmoid (/ (abs (- rt_low *rtlow*))
+                        *time-rt*)))
+(defun fitness-rt_high (rt_high)
+  (gems:half-sigmoid (/ (abs (- rt_high *rthigh*))
+                        *time-rt*)))
+
+(defun evaluate-fitness (individual)
+  "Return the values (fitness extras) for given individual"
+  (let* ((program (gems:individual-tree individual))
+         (results (run-experiment program))
+         (acc_low (alexandria:mean (filter-non-negative (mapcar #'result-acc_low results))))
+         (f-acc_low (fitness-acc_low acc_low)) 
+         (acc_high (alexandria:mean (filter-non-negative (mapcar #'result-acc_high results))))
+         (f-acc_high (fitness-acc_high acc_high))
+         (rt_low (alexandria:mean (filter-non-negative (mapcar #'result-rt_low results))))
+         (f-rt_low (fitness-rt_low rt_low)) 
+         (rt_high (alexandria:mean (filter-non-negative (mapcar #'result-rt_high results))))
+         (f-rt_high (fitness-rt_high rt_high))
+         (sd_low (alexandria:standard-deviation (filter-non-negative (mapcar #'result-rt_low results))))
+         (f-sd_low (fitness-sd_low sd_low)) 
+         (sd_high (alexandria:standard-deviation (filter-non-negative (mapcar #'result-rt_high results))))
+         (f-sd_high (fitness-sd_high sd_high))
+         (program-size (gems:program-size program))
+         (f-s (fitness-size program-size))
+         )
+    (values (+ (* *fitness-rt_low* f-rt_low)
+               (* *fitness-rt_high* f-rt_high)
+               (* *fitness-sd_low* f-sd_low)
+               (* *fitness-sd_high* f-sd_high)
+               (* *fitness-acc_low* f-acc_low)
+               (* *fitness-acc_high* f-acc_high)
+               (* *propn-fitness-size* f-s)
+               )
+            (list rt_low f-rt_low rt_high f-rt_high acc_low f-acc_low  program-size f-s))))
+;; -- run-go template
+;;    Complete the run-gp code, or use your own functions to 
+;;    call the GP code.
+                                        ;(defun run-gp ()
+                                        ;  (gems:launch (operator-set) #'evaluate-fitness :total-generations 1000 :population-size 100)
+                                        ;  )
+                                        ;(run-gp)
+(defun run-gp (&key (max-generations *max-generations*)
+                 (population-size *population-size*)
+                 (f-rt_low *fitness-rt_low*) 
+                 (f-rt_high *fitness-rt_high*)
+                 (f-sd_low *fitness-sd_low*) 
+                 (f-sd_high *fitness-sd_high*)
+                 (f-acc_low *fitness-acc_low*)
+                 (f-acc_high *fitness-acc_high*)
+                 (rt *time-rt*)
+                 (logger nil) ; logger function
+                 )
+  "Sets all relevant parameters for the GP system and model evaluation"
+  (setf *fitness-rt_low* f-rt_low
+        *fitness-rt_high* f-rt_high
+        *fitness-sd_low* f-sd_low
+        *fitness-sd_high* f-sd_high
+        *fitness-acc_low* f-acc_low
+        *fitness-acc_high* f-acc_high
+        *time-rt* rt)
+  (gems:launch (operator-set) #'evaluate-fitness
+               :total-generations *max-generations*
+               :population-size *population-size*
+               :logger logger))
+               
+(defun make-filename (base args extn)
+  "Creates a filename with 'args' spliced into name"
+  (uiop:strcat (remove #\. (format nil "~a~{-~$~}" base args))
+               extn))
+               
+;; (defun log-name-section-4 ()
+;;   (make-filename "log" (list) ".csv"))
+  
+          
+(dolist (defn '(()))
+    (run-gp :max-generations *max-generations*
+            :population-size *population-size*
+            :rt *time-rt*
+            :logger 
+(gems:combine-loggers
+  (gems:make-logger "log.csv")            ; 1
+  (gems:make-logger "final-population.yml"     ; 2
+                    :kind :trace
+                    :filter #'(lambda (generation-number) (= generation-number *max-generations*))
+                    ))))
+
+
+                                             
+(dolist (defn '(()))
+  (let ((results (gpstats:read-trace (make-filename "final-population" defn ".yml"))))
+    (gpstats:write-fitness-generations results (make-filename "fitness" defn ""))
+                                        ; data for heatmap of model similarity
+    (gpstats:write-similarity-generations results (make-filename "similarity" defn ""))
+                                        ; data for proportions of dead code in population
+    (gpstats:write-deadcode-generations results (make-filename "sample-ndc" defn "")
+                                        #'run-experiment)
+    (let* ((last-generation (rest (first (last results))))
+           (best-models (gpstats:best-individuals-in-generation last-generation))
+           (best-ndc-models (gpstats:clean-individuals best-models #'run-experiment)))   ; 1
+      (gpstats:write-similarity-individuals best-ndc-models
+                                            (make-filename "best" defn ".dat"))
+      (format t "Found ~a best models in final generation, with ~a unique, cleaned models.~&"
+              (length best-models) (length best-ndc-models))
+      (print best-ndc-models)))
+  )

+ 595 - 0
published-results/sgai-2023-heuristics/without_noise/task_without_noise.lisp

@@ -0,0 +1,595 @@
+(require :asdf)
+(require :gems)
+(require :lhstats)
+
+(defparameter $inputT 100)    ;; perception + attend
+(defparameter $outputT 140)   ;; intend + movement
+(defparameter $cogT 70)       ;; basic cognitive process
+(defparameter $stmT 50)       ;; basic STM process
+(defparameter $syntaxT 0)     ;; prog2, if      ;; these are different from cognitive operators
+
+(defparameter stored-state (make-random-state t))
+(setf *random-state* stored-state)
+
+;(print stored-state)
+ 
+
+(defvar *max-generations* 500)
+(defvar *population-size* 500)
+
+(defvar *fitness-rt_low* 0.35)   
+(defvar *fitness-rt_high* 0.35)       
+(defvar *fitness-sd_low* 0)  
+(defvar *fitness-sd_high* 0)      
+(defvar *fitness-acc_low* 0.3)       
+(defvar *fitness-acc_high* 0)   
+(defvar *propn-fitness-size* 0)     
+
+(defvar *time-rt* 714)            
+(defvar *rtlow* 784)            
+(defvar *rthigh* 704)           
+(defvar *sdlow* 294)            
+(defvar *sdhigh* 242)          
+(defvar *acclow* 1)         
+(defvar *acchigh* 1)          
+(defvar *size-ps* 100)          
+
+
+(defstruct model
+  "Defines the state of a model and its interactions with the environment"
+  clock     ; core model   
+  current
+  stm
+  (attFocus :centre) ;; focus of attention; default is centre
+  salient   ;; salient is what is salient in the visual field.
+  response  
+  inputs    ;; buffers for inputs
+  _screenLeft
+  _screenCentre
+  _screenRight
+  ( _inputName  "_")
+  ( _type :stimulus)
+  )
+  
+(defstruct input
+  (name nil)
+  (location :central)
+  (type :stimulus))
+  
+(defstruct result inputs timing response rt_low rt_high acc_low acc_high)
+
+(defun detect-main (md)
+  (let ((current-stimuli (timeline (model-clock md)))   
+        (att (model-attFocus md))
+        (stim nil))
+    (setf stim (first (find-if #'(lambda (y)
+                                   (eq (second y) att))
+                               current-stimuli)))
+    (setf (model-_inputname md) (subseq (symbol-name stim) 0 3))
+    (case att
+      (left   (setf (model-_screenLeft   md) stim))
+      (centre (setf (model-_screenCentre md) stim))
+      (right  (setf (model-_screenRight  md) stim)))))
+      
+(defun timeline (time)
+  (cond
+    ((and (>= time 0) (< time 10000)) '(stim1 centre))
+    (t nil)))
+;; These convenience functions allow interpret/display-pseudocode to
+;; work with both s-expressions and syntax-tree:node structures.
+(defun operator-label (operator)
+  "Returns label of given operator"
+  (typecase operator
+    (list 
+     (intern (symbol-name (first operator)) "KEYWORD"))
+    (syntax-tree:node
+     (intern (symbol-name (syntax-tree:node-label operator)) "KEYWORD"))
+    (otherwise
+     (error "Invalid operator type"))))
+     
+(defun operator-children (operator)
+  "Returns children of given operator"
+  (typecase operator
+    (list 
+     (rest operator))
+    (syntax-tree:node
+     (syntax-tree:node-children operator))
+    (otherwise
+     (error "Invalid operator type"))))
+;; interpret function evaluates the given operator (program), in the context of
+;; md, which is a model+environment definition
+(defun interpret (operator md)                                                
+  (when (syntax-tree:node-p operator) (incf (syntax-tree:node-entries operator))) 
+  (unless  (> (model-clock md) 10000) 
+    (case (operator-label operator)
+    
+      (:put-stm
+       (incf (model-clock md) $cogT)
+       (setf (model-stm md) 
+             (if (= 3 (length (model-stm md)))
+                 (list (model-current md)
+                       (first (model-stm md))
+                       (second (model-stm md)))
+                 (cons (model-current md)
+                       (model-stm md)))))
+                       
+      (:detect
+       (incf (model-clock md) $inputT)
+       (setf (model-salient md) (model-inputs md)))
+       
+      (:sample
+       (incf (model-clock md) $inputT)
+           (setf (model-current md) (model-salient md))
+           
+             )
+           
+           
+       
+       
+     
+       
+      (:respond
+       (incf (model-clock md) $outputT)
+       (setf (model-response md)
+             (if (< (random 1.0) 1)  (model-current md)  0)))
+
+      (:conservative
+       (incf (model-clock md) $outputT)
+       (setf (model-response md)
+       (cond ( (> (model-current md) 3) 
+                 1)
+          		((< (model-current md) -3)
+                 2)
+          		((and (< (model-current md) 3)  (> (model-current md) -3) ) 
+                   0)
+
+             )))   
+
+
+      (:threshold
+       (incf (model-clock md) $outputT)
+       (setf (model-response md)
+       (cond ( (> (model-current md) 1.2) 
+                 1)
+          		((<= (model-current md) 1.2)
+                 0)
+
+             )))   
+
+            (:respond_low
+       (incf (model-clock md) $outputT)
+       (setf (model-response md)
+             (if (< (model-current md) -3)  2  0)))      
+             
+      (:respondrandom
+       (incf (model-clock md) $outputT)
+       (setf (model-response md)
+             (if (< (random 1.0) .5) 1  2)))
+             
+      (:respond6
+       (incf (model-clock md) $outputT)
+       (setf (model-response md)
+             (if (< (random 1.0) .6)  (model-current md)  0)))
+             
+      (:respond7
+       (incf (model-clock md) $outputT)
+       (setf (model-response md)
+             (if (< (random 1.0) .7)  (model-current md)  0)))
+             
+      (:respond8
+       (incf (model-clock md) $outputT)
+       (setf (model-response md)
+             (if (< (random 1.0) .8)  (model-current md)  0)))
+      (:respond9
+       (incf (model-clock md) $outputT)
+       (setf (model-response md)
+             (if (< (random 1.0) .9)  (model-current md)  0)))
+             
+      (:ifM1
+       (incf (model-clock md) $syntaxT)
+       (if  (eq 1 (model-current md)) ; 0 is false, other numbers true
+            (interpret (first (operator-children operator)) md)
+            ))
+            
+      (:ifM2
+       (incf (model-clock md) $syntaxT)
+       (if  (eq 2 (model-current md)) ; 0 is false, other numbers true
+            (interpret (first (operator-children operator)) md)
+            ))
+            
+      (:if-mc
+       (incf (model-clock md) $syntaxT)
+       
+        (if  (eq 1 (model-current md)) ; 0 is false, other numbers true
+            (interpret (first (operator-children operator)) md)
+            (interpret (second (operator-children operator)) md)
+            )
+       
+       )
+      
+      (:ifosidf
+        (incf (model-clock md) $syntaxT)
+        (if (interpret (first (operator-children operator)) md)
+            (interpret (second (operator-children operator)) md)
+            (interpret (third (operator-children operator)) md)))
+            
+      (:prog2
+          (incf (model-clock md) $syntaxT)
+          (interpret (first (operator-children operator)) md)
+        (interpret (second (operator-children operator)) md))
+        
+      (:donothing
+       (incf (model-clock md) 0))
+       
+      (:wait-25
+       (incf (model-clock md) 25))
+      
+      (:wait-50
+       (incf (model-clock md) 50))
+      
+      (:wait-100
+       (incf (model-clock md) 100))
+      
+      (:wait-200
+       (incf (model-clock md) 200))
+      
+      (:wait-1000
+       (incf (model-clock md) 1000))
+      
+      (:wait-1500
+       (incf (model-clock md) 1500))
+       
+      (:prog3
+       (incf (model-clock md) $syntaxT)
+       (interpret (first (operator-children operator)) md)
+       (interpret (second (operator-children operator)) md)
+       (interpret (third (operator-children operator)) md))
+       
+      (:prog4
+       (incf (model-clock md) $syntaxT)
+       (interpret (first (operator-children operator)) md)
+       (interpret (second (operator-children operator)) md)
+       (interpret (third (operator-children operator)) md)
+       (interpret (fourth (operator-children operator)) md))
+       
+      (:dotimes-2
+       (incf (model-clock md) $syntaxT)
+       (dotimes (i 2)
+         (interpret (first (operator-children operator)) md)))
+      
+      (:dotimes-3 
+       (incf (model-clock md) $syntaxT)
+       (dotimes (i 3)
+         (interpret (first (operator-children operator)) md)))
+      
+      (:dotimes-5 
+       (incf (model-clock md) $syntaxT)
+       (dotimes (i 5)
+         (interpret (first (operator-children operator)) md)))
+         
+      (:dotimes-10
+       (incf (model-clock md) $syntaxT)
+       (dotimes (i 10)
+         (interpret (first (operator-children operator)) md)))
+         
+      (:while-50
+       (incf (model-clock md) $syntaxT)
+       (let ((start-time (model-clock md)))
+         (do ((x 1 (incf x)))  ;; as a safeguard...
+             ((or (>= (model-clock md) (+ start-time 50))
+                  (> x 4))) ;; as a safeguard...
+           (interpret (first (operator-children operator)) md))))
+      
+      (:while-100 
+       (incf (model-clock md) $syntaxT)
+       (let ((start-time (model-clock md)))
+         (do ((x 1 (incf x)))  ;; as a safeguard...
+             ((or (>= (model-clock md) (+ start-time 100))
+                  (> x 4))) ;; as a safeguard...
+           (interpret (first (operator-children operator)) md))))
+      
+      (:while-200 
+       (incf (model-clock md) $syntaxT)
+       (let ((start-time (model-clock md)))
+         (do ((x 1 (incf x)))  ;; as a safeguard...
+             ((or (>= (model-clock md) (+ start-time 200))
+                  (> x 5))) ;; as a safeguard...
+           (interpret (first (operator-children operator)) md))))
+      
+      
+      (:access-stm-1 
+       (incf (model-clock md) $stmT)
+       (setf (model-current md) (first (model-stm md))))
+       
+      (:access-stm-2
+       (incf (model-clock md) $stmT)
+       (setf (model-current md) (second (model-stm md))))
+       
+      (:access-stm-3
+       (incf (model-clock md) $stmT)
+       (setf (model-current md) (third (model-stm md))))
+       
+      (:compare-1-2
+       (incf (model-clock md) $cogT)
+       (setf (model-current md)
+             (if (= (first (model-stm md)) (second (model-stm md))) (first (model-stm md))  (model-current md))))
+             
+      (:compare-2-3
+       (incf (model-clock md) $cogT)
+       (setf (model-current md)
+             (if (= (second (model-stm md)) (third (model-stm md))) (second (model-stm md))  (model-current md))))
+             
+      (:compare-1-3
+       (incf (model-clock md) $cogT)
+       (setf (model-current md)
+             (if (= (first (model-stm md)) (third (model-stm md))) (first (model-stm md))  (model-current md))))
+
+
+    (:sum-1-2
+       (incf (model-clock md) $cogT)
+       (setf (model-current md)
+             (+ (first (model-stm md))  (second (model-stm md))))
+             
+             
+             )
+             
+(:boost
+       (incf (model-clock md) $cogT)
+       (setf (model-current md)
+             (+ (model-current md)  1))
+          
+             )
+(:decay
+       (incf (model-clock md) $cogT)
+       (setf (model-current md)
+             (* (model-current md)  0))
+             
+            
+             )
+      (:sum-1-3
+       (incf (model-clock md) $cogT)
+       (setf (model-current md)
+             (+ (first (model-stm md))  (third (model-stm md))))
+            )
+
+              (:sum-2-3
+       (incf (model-clock md) $cogT)
+       (setf (model-current md)
+             (+ (second (model-stm md))  (third (model-stm md))))
+            )
+
+     (:if
+       (incf (model-clock md) $syntaxT)
+       (if (> (model-current md) 1.2)
+       (interpret (first (operator-children operator)) md)
+       (interpret (second (operator-children operator)) md)))
+
+
+
+      
+      (otherwise ; error if comes across an unknown operator
+       (error "interpret: unknown operator ~a" (operator-label operator))))))
+;; operator set (name . number-of-children)
+;; TIP: comment out individual lines to ignore specific operators
+(defun operator-set () 
+  '(
+    (ACCESS-STM-1 . 0)
+    (ACCESS-STM-2 . 0)
+    (ACCESS-STM-3 . 0)
+    ;(COMPARE-1TO3 . 0)
+    (COMPARE-1-2 . 0)
+    (COMPARE-1-3 . 0)
+    (COMPARE-2-3 . 0)
+    (SUM-1-2 . 0)
+    ;(SUM-1-3 . 0)
+    ;(SUM-2-3 . 0)
+    ;(DETECT . 0)
+    ;(WHILE-50 . 1)
+    ;(WHILE-100 . 1)
+    ;(WHILE-200 . 1)
+    (PUT-STM . 0)
+    (SAMPLE . 0)
+    ;(BOOST . 0)
+    ;(DECAY . 0)
+    ;(SAMPLE4 . 0)
+    (THRESHOLD . 0)
+    ;(CONSERVATIVE . 0)
+    ;(RESPONDRANDOM . 0)
+    ;(RESPOND6 . 0)
+    ;(RESPOND7 . 0)
+    ;(RESPOND8 . 0)
+    ;(RESPOND9 . 0)
+    (WAIT-50 . 0)
+    (WAIT-25 . 0)
+    (WAIT-100 . 0)
+    (WAIT-200 . 0)
+    (WAIT-1000 . 0)
+    (WAIT-1500 . 0)
+    (DONOTHING . 0)
+    (DOTIMES-2 . 1)
+    (DOTIMES-3 . 1)
+    (DOTIMES-5 . 1)
+    (DOTIMES-10 . 1)
+    (PROG2 . 2)
+    (PROG3 . 3)
+    (PROG4 . 4)
+    (IF . 2)
+    ;(IF_NC . 2)
+    ;(IF_CURRENT_TH . 3)
+    ;(IF_CURRENT_CON . 3)  
+    ))
+;; 1 is high magnitude, 2 is low magnitude
+
+(defparameter stimuli '(
+                         .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 
+                        .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 
+                        .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 
+                        .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 
+                        .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 
+                        .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 
+                        .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 
+                        .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 
+                        .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 
+                        .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 
+                        .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 
+                        .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 
+                        .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 
+                        .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 
+                        .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 .6 .75 
+                        
+                        ))
+;; -- run-experiment template
+;;    Complete the run-experiment code. 
+;; -- output: The return value(s) is used by 'evaluate-fitness' below.
+
+(defun run-experiment (program) 
+  (let ((results '())
+        (expt-stimuli (statistics:random-sample (length stimuli) stimuli)))
+    (dolist (input expt-stimuli)
+      ;; -- begin single trial
+      (let ((md (make-model :clock 0 :current 0 :stm '(0 0 0)
+                            :salient input :inputs input :response 0)))
+        (interpret program md)
+        ;; -- collect information and create result
+        (let ((result (make-result :timing 0 :inputs input :response 0 :rt_low 0 :rt_high 0 :acc_low 0 :acc_high 0)))
+          (setf (result-response result) (model-response md))           ; record model's response
+                    (setf (result-acc_low result) (model-response md))           ; record model's response
+
+          (if (eq (model-inputs md) .6)
+              (setf (result-rt_low result) (model-clock md) (result-rt_high result) -1000)
+              (setf (result-rt_high result) (model-clock md) (result-rt_low result) -1000))
+          (push result results))))
+    results))
+    
+(defun filter-non-negative (l)
+  (cond
+    ((null l)                   ;; empty list
+     nil)
+    ((>= (first l) 0)      ;; number >= 0
+     (cons (first l) (filter-non-negative (rest l))))
+    (t                         ;; all other cases 
+     (filter-non-negative (rest l)))))
+     
+(defun fitness-acc_low (acc_low)
+  (abs (- *acclow* acc_low)))
+(defun fitness-acc_high (acc_high)
+  (abs (- *acchigh* acc_high)))
+
+;; Computes the f_s objective function.
+(defun fitness-size (program-size)
+  (gems:half-sigmoid (/ program-size *size-ps*)))
+
+(defun fitness-sd_low (sd_low)
+  (gems:half-sigmoid (/ (abs (- sd_low *sdlow*))
+                        *time-rt*)))
+  
+(defun fitness-sd_high (sd_high)
+  (gems:half-sigmoid (/ (abs (- sd_high *sdhigh*))
+                        *time-rt*)))
+  
+(defun fitness-rt_low (rt_low)
+  (gems:half-sigmoid (/ (abs (- rt_low *rtlow*))
+                        *time-rt*)))
+(defun fitness-rt_high (rt_high)
+  (gems:half-sigmoid (/ (abs (- rt_high *rthigh*))
+                        *time-rt*)))
+
+(defun evaluate-fitness (individual)
+  "Return the values (fitness extras) for given individual"
+  (let* ((program (gems:individual-tree individual))
+         (results (run-experiment program))
+         (acc_low (alexandria:mean (filter-non-negative (mapcar #'result-acc_low results))))
+         (f-acc_low (fitness-acc_low acc_low)) 
+         (acc_high (alexandria:mean (filter-non-negative (mapcar #'result-acc_high results))))
+         (f-acc_high (fitness-acc_high acc_high))
+         (rt_low (alexandria:mean (filter-non-negative (mapcar #'result-rt_low results))))
+         (f-rt_low (fitness-rt_low rt_low)) 
+         (rt_high (alexandria:mean (filter-non-negative (mapcar #'result-rt_high results))))
+         (f-rt_high (fitness-rt_high rt_high))
+         (sd_low (alexandria:standard-deviation (filter-non-negative (mapcar #'result-rt_low results))))
+         (f-sd_low (fitness-sd_low sd_low)) 
+         (sd_high (alexandria:standard-deviation (filter-non-negative (mapcar #'result-rt_high results))))
+         (f-sd_high (fitness-sd_high sd_high))
+         (program-size (gems:program-size program))
+         (f-s (fitness-size program-size))
+         )
+    (values (+ (* *fitness-rt_low* f-rt_low)
+               (* *fitness-rt_high* f-rt_high)
+               (* *fitness-sd_low* f-sd_low)
+               (* *fitness-sd_high* f-sd_high)
+               (* *fitness-acc_low* f-acc_low)
+               (* *fitness-acc_high* f-acc_high)
+               (* *propn-fitness-size* f-s)
+               )
+            (list rt_low f-rt_low rt_high f-rt_high acc_low f-acc_low  program-size f-s))))
+;; -- run-go template
+;;    Complete the run-gp code, or use your own functions to 
+;;    call the GP code.
+                                        ;(defun run-gp ()
+                                        ;  (gems:launch (operator-set) #'evaluate-fitness :total-generations 1000 :population-size 100)
+                                        ;  )
+                                        ;(run-gp)
+(defun run-gp (&key (max-generations *max-generations*)
+                 (population-size *population-size*)
+                 (f-rt_low *fitness-rt_low*) 
+                 (f-rt_high *fitness-rt_high*)
+                 (f-sd_low *fitness-sd_low*) 
+                 (f-sd_high *fitness-sd_high*)
+                 (f-acc_low *fitness-acc_low*)
+                 (f-acc_high *fitness-acc_high*)
+                 (rt *time-rt*)
+                 (logger nil) ; logger function
+                 )
+  "Sets all relevant parameters for the GP system and model evaluation"
+  (setf *fitness-rt_low* f-rt_low
+        *fitness-rt_high* f-rt_high
+        *fitness-sd_low* f-sd_low
+        *fitness-sd_high* f-sd_high
+        *fitness-acc_low* f-acc_low
+        *fitness-acc_high* f-acc_high
+        *time-rt* rt)
+  (gems:launch (operator-set) #'evaluate-fitness
+               :total-generations *max-generations*
+               :population-size *population-size*
+               :logger logger))
+               
+(defun make-filename (base args extn)
+  "Creates a filename with 'args' spliced into name"
+  (uiop:strcat (remove #\. (format nil "~a~{-~$~}" base args))
+               extn))
+               
+;; (defun log-name-section-4 ()
+;;   (make-filename "log" (list) ".csv"))
+  
+          
+(dolist (defn '(()))
+    (run-gp :max-generations *max-generations*
+            :population-size *population-size*
+            :rt *time-rt*
+            :logger 
+(gems:combine-loggers
+  (gems:make-logger "log.csv")            ; 1
+  (gems:make-logger "final-population.yml"     ; 2
+                    :kind :trace
+                    :filter #'(lambda (generation-number) (= generation-number *max-generations*))
+                    ))))
+
+
+                                             
+(dolist (defn '(()))
+  (let ((results (gpstats:read-trace (make-filename "final-population" defn ".yml"))))
+    (gpstats:write-fitness-generations results (make-filename "fitness" defn ""))
+                                        ; data for heatmap of model similarity
+    (gpstats:write-similarity-generations results (make-filename "similarity" defn ""))
+                                        ; data for proportions of dead code in population
+    (gpstats:write-deadcode-generations results (make-filename "sample-ndc" defn "")
+                                        #'run-experiment)
+    (let* ((last-generation (rest (first (last results))))
+           (best-models (gpstats:best-individuals-in-generation last-generation))
+           (best-ndc-models (gpstats:clean-individuals best-models #'run-experiment)))   ; 1
+      (gpstats:write-similarity-individuals best-ndc-models
+                                            (make-filename "best" defn ".dat"))
+      (format t "Found ~a best models in final generation, with ~a unique, cleaned models.~&"
+              (length best-models) (length best-ndc-models))
+      (print best-ndc-models)))
+  )