<commit_before>;; checksum.clj
;; <NAME>, <EMAIL>
;;
;; Problem Source: Program Repair Benchmark Paper (add citation later)
;;
;; Given a string (max length 50), compute the integer values of the characters
;; in the string, sum them, take the sum modulo 64, add the value of the \space 
;; character, and then convert that integer back into its corresponding character
;; (the checksum). Program must print "Check sum is X", where X is replaced by
;; the correct checksum.
;;
;; input stack has the input string

(ns clojush.problems.software.checksum
  (:use clojush.pushgp.pushgp
        [clojush pushstate interpreter random util globals]
        clojush.instructions.tag
        clojure.math.numeric-tower
        ))

; Atom generators
(def checksum-atom-generators
  (concat (list
            "Check sum is "
            \space
            64
            ;;; end constants
            (fn [] (- (lrand-int 257) 128)) ;Integer ERC [-128,128]
            (fn [] (lrand-nth (concat [\newline \tab] (map char (range 32 127))))) ;Visible character ERC
            ;;; end ERCs
            (tag-instruction-erc [:exec :integer :boolean :string :char] 1000)
            (tagged-instruction-erc 1000)
            ;;; end tag ERCs
            'in1
            ;;; end input instructions
            )
          (registered-for-stacks [:integer :boolean :string :char :exec :print])))


;; Define test cases
(defn checksum-input
  "Makes a checksum input of length len."
  [len]
  (apply str
         (repeatedly len
                     #(lrand-nth (concat [\newline \tab]
                                         (map char (range 32 127)))))))

;; A list of data domains for the problem. Each domain is a vector containing
;; a "set" of inputs and two integers representing how many cases from the set
;; should be used as training and testing cases respectively. Each "set" of
;; inputs is either a list or a function that, when called, will create a
;; random element of the set.
(def checksum-data-domains
  [[(list "", "A", "\t", "\n", "B\n", "\n\n",
          (apply str (repeat 50 \newline))
          (apply str (repeat 50 \space))
          (apply str (repeat 50 \s))
          (apply str (take 50 (cycle (list \C \D \newline))))
          (apply str (take 50 (cycle (list \x \newline \y \space))))
          (apply str (take 50 (cycle (list \space \newline))))) 22 0] ;; "Special" inputs covering some base cases
   [(fn [] (checksum-input (inc (lrand-int 50)))) 88 1000]
   ])


(defn test-and-train-data-from-domains
  "Takes a list of domains and creates a set of (random) train inputs and a set of test
   inputs based on the domains. Returns [train test]. A program should not
   be considered a solution unless it is perfect on both the train and test
   cases."
  [domains]
  (apply mapv concat (map (fn [[input-set n-train n-test]]
                            (if (fn? input-set)
                              (vector (repeatedly n-train input-set)
                                      (repeatedly n-test input-set))
                              (vector (if (>= n-train (count input-set))
                                        input-set
                                        (take n-train (shuffle input-set)))
                                      (if (>= n-test (count input-set))
                                        input-set
                                        (take n-test (shuffle input-set))))))
                          domains)))

;;Can make checksum test data like this:
;(test-and-train-data-from-domains checksum-data-domains)

; Helper function for error function
(defn checksum-test-cases
  "Takes a sequence of inputs and gives IO test cases of the form
   [input output]."
  [inputs]
  (map #(vector %
                (format "Check sum is %c"
                        (char (+ (mod (apply + (map int %)) 64)
                                 (int \space)))))
       inputs))

; Define error function. For now, each run uses different random inputs
(defn checksum-error-function
  "Returns the error function for the checksum problem. Takes as
   input checksum data domains."
  [data-domains]
  (let [[train-cases test-cases] (map checksum-test-cases
                                      (test-and-train-data-from-domains data-domains))]
    (when true ;; Change to false to not print test cases
      (doseq [[i case] (map vector (range) train-cases)]
        (println (format "Train Case: %3d | Input/Output: %s" i (str case))))
      (doseq [[i case] (map vector (range) test-cases)]
        (println (format "Test Case: %3d | Input/Output: %s" i (str case)))))
    (fn the-actual-checksum-error-function
      ([program]
        (the-actual-checksum-error-function program :train))
      ([program data-cases] ;; data-cases should be :train or :test
        (the-actual-checksum-error-function program data-cases false))
      ([program data-cases print-outputs]
        (let [behavior (atom '())
              errors (doall
                       (for [[input correct-output] (case data-cases
                                                                 :train train-cases
                                                                 :test test-cases
                                                                 [])]
                         (let [final-state (run-push program
                                                     (->> (make-push-state)
                                                       (push-item input :input)
                                                       (push-item "" :output)))
                               printed-result (stack-ref :output 0 final-state)]
                           (when print-outputs
                             (println (format "Correct output: %-19s | Program output: %-19s" correct-output printed-result)))
                           ; Record the behavior
                           (when @global-print-behavioral-diversity
                             (swap! behavior conj printed-result))
                           ; Error is Levenshtein distance
                           (levenshtein-distance correct-output printed-result))))]
          (when @global-print-behavioral-diversity
            (swap! population-behaviors conj @behavior))
          errors)))))

(defn checksum-report
  "Custom generational report."
  [best population generation error-function report-simplifications]
  (let [best-program (not-lazy (:program best))
        best-test-errors (error-function best-program :test)
        best-total-test-error (apply +' best-test-errors)]
    (println ";;******************************")
    (printf ";; -*- Checksum problem report - generation %s\n" generation)(flush)
    (println "Test total error for best:" best-total-test-error)
    (println (format "Test mean error for best: %.5f" (double (/ best-total-test-error (count best-test-errors)))))
    (when (zero? (:total-error best))
      (doseq [[i error] (map vector
                             (range)
                             best-test-errors)]
        (println (format "Test Case  %3d | Error: %d" i error))))
    (println ";;------------------------------")
    (println "Outputs of best individual on training cases:")
    (error-function best-program :train true)
    (println ";;******************************")
    )) ;; To do validation, could have this function return an altered best individual
       ;; with total-error > 0 if it had error of zero on train but not on validation
       ;; set. Would need a third category of data cases, or a defined split of training cases.


; Define the argmap
(def argmap<commit_msg>Added second fitness case for distance of last character<commit_after>;; checksum.clj
;; <NAME>, <EMAIL>
;;
;; Problem Source: Program Repair Benchmark Paper (add citation later)
;;
;; Given a string (max length 50), compute the integer values of the characters
;; in the string, sum them, take the sum modulo 64, add the value of the \space 
;; character, and then convert that integer back into its corresponding character
;; (the checksum). Program must print "Check sum is X", where X is replaced by
;; the correct checksum.
;;
;; input stack has the input string

(ns clojush.problems.software.checksum
  (:use clojush.pushgp.pushgp
        [clojush pushstate interpreter random util globals]
        clojush.instructions.tag
        clojure.math.numeric-tower
        ))

; Atom generators
(def checksum-atom-generators
  (concat (list
            "Check sum is "
            \space
            64
            ;;; end constants
            (fn [] (- (lrand-int 257) 128)) ;Integer ERC [-128,128]
            (fn [] (lrand-nth (concat [\newline \tab] (map char (range 32 127))))) ;Visible character ERC
            ;;; end ERCs
            (tag-instruction-erc [:exec :integer :boolean :string :char] 1000)
            (tagged-instruction-erc 1000)
            ;;; end tag ERCs
            'in1
            ;;; end input instructions
            )
          (registered-for-stacks [:integer :boolean :string :char :exec :print])))


;; Define test cases
(defn checksum-input
  "Makes a checksum input of length len."
  [len]
  (apply str
         (repeatedly len
                     #(lrand-nth (concat [\newline \tab]
                                         (map char (range 32 127)))))))

;; A list of data domains for the problem. Each domain is a vector containing
;; a "set" of inputs and two integers representing how many cases from the set
;; should be used as training and testing cases respectively. Each "set" of
;; inputs is either a list or a function that, when called, will create a
;; random element of the set.
(def checksum-data-domains
  [[(list "", "A", "\t", "\n", "B\n", "\n\n",
          (apply str (repeat 50 \newline))
          (apply str (repeat 50 \space))
          (apply str (repeat 50 \s))
          (apply str (take 50 (cycle (list \C \D \newline))))
          (apply str (take 50 (cycle (list \x \newline \y \space))))
          (apply str (take 50 (cycle (list \space \newline))))) 22 0] ;; "Special" inputs covering some base cases
   [(fn [] (checksum-input (inc (lrand-int 50)))) 88 1000]
   ])


(defn test-and-train-data-from-domains
  "Takes a list of domains and creates a set of (random) train inputs and a set of test
   inputs based on the domains. Returns [train test]. A program should not
   be considered a solution unless it is perfect on both the train and test
   cases."
  [domains]
  (apply mapv concat (map (fn [[input-set n-train n-test]]
                            (if (fn? input-set)
                              (vector (repeatedly n-train input-set)
                                      (repeatedly n-test input-set))
                              (vector (if (>= n-train (count input-set))
                                        input-set
                                        (take n-train (shuffle input-set)))
                                      (if (>= n-test (count input-set))
                                        input-set
                                        (take n-test (shuffle input-set))))))
                          domains)))

;;Can make checksum test data like this:
;(test-and-train-data-from-domains checksum-data-domains)

; Helper function for error function
(defn checksum-test-cases
  "Takes a sequence of inputs and gives IO test cases of the form
   [input output]."
  [inputs]
  (map #(vector %
                (format "Check sum is %c"
                        (char (+ (mod (apply + (map int %)) 64)
                                 (int \space)))))
       inputs))

; Define error function. For now, each run uses different random inputs
(defn checksum-error-function
  "Returns the error function for the checksum problem. Takes as
   input checksum data domains."
  [data-domains]
  (let [[train-cases test-cases] (map checksum-test-cases
                                      (test-and-train-data-from-domains data-domains))]
    (when true ;; Change to false to not print test cases
      (doseq [[i case] (map vector (range) train-cases)]
        (println (format "Train Case: %3d | Input/Output: %s" i (str case))))
      (doseq [[i case] (map vector (range) test-cases)]
        (println (format "Test Case: %3d | Input/Output: %s" i (str case)))))
    (fn the-actual-checksum-error-function
      ([program]
        (the-actual-checksum-error-function program :train))
      ([program data-cases] ;; data-cases should be :train or :test
        (the-actual-checksum-error-function program data-cases false))
      ([program data-cases print-outputs]
        (let [behavior (atom '())
              errors (flatten
                       (doall
                         (for [[input correct-output] (case data-cases
                                                                   :train train-cases
                                                                   :test test-cases
                                                                   [])]
                           (let [final-state (run-push program
                                                       (->> (make-push-state)
                                                         (push-item input :input)
                                                         (push-item "" :output)))
                                 printed-result (stack-ref :output 0 final-state)]
                             (when print-outputs
                               (println (format "Correct output: %-19s | Program output: %-19s" correct-output printed-result)))
                             ; Record the behavior
                             (when @global-print-behavioral-diversity
                               (swap! behavior conj printed-result))
                             ; Error is Levenshtein distance and, if correct format, distance from correct character
                             (vector
                               (levenshtein-distance correct-output printed-result)
                               (if (and (= (count correct-output) (count printed-result))
                                        (= "Check sum is " (subs printed-result 0 (count "Check sum is "))))
                                 (abs (- (int (last correct-output)) (int (last printed-result)))) ;distance from correct last character
                                 1000) ;penalty for wrong format
                               )))))]
          (when @global-print-behavioral-diversity
            (swap! population-behaviors conj @behavior))
          errors)))))

(defn checksum-report
  "Custom generational report."
  [best population generation error-function report-simplifications]
  (let [best-program (not-lazy (:program best))
        best-test-errors (error-function best-program :test)
        best-total-test-error (apply +' best-test-errors)]
    (println ";;******************************")
    (printf ";; -*- Checksum problem report - generation %s\n" generation)(flush)
    (println "Test total error for best:" best-total-test-error)
    (println (format "Test mean error for best: %.5f" (double (/ best-total-test-error (count best-test-errors)))))
    (when (zero? (:total-error best))
      (doseq [[i error] (map vector
                             (range)
                             best-test-errors)]
        (println (format "Test Case  %3d | Error: %d" i error))))
    (println ";;------------------------------")
    (println "Outputs of best individual on training cases:")
    (error-function best-program :train true)
    (println ";;******************************")
    )) ;; To do validation, could have this function return an altered best individual
       ;; with total-error > 0 if it had error of zero on train but not on validation
       ;; set. Would need a third category of data cases, or a defined split of training cases.


; Define the argmap
(def argmap
{'fraction_non_alphanumeric': 0.1175139822786401, 'fraction_numerical': 0.009363413561239238, 'mean_word_length': 1.7037037037037037, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 17, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 52, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6260157', 'n_tokens_mistral': 4232, 'n_tokens_neox': 3972, 'n_words': 1500}