;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; calc-fast-evolve.lisp -- code linking lgp2.lisp and calc-fast.lisp ;; to evolve programs for a simulated stack-based calculator ;; ;; c) 1999, Lee Spector ;; Modified by Alan Robinson to work with LGP2 ;; Subesquently re-tweaked by Lee Spector for formatting and to make ;; Alan's example work better (changed some parameters). ;; ;; version 1.19991019 (n.yyyymmdd) #| This is a version of calc-evolve.lisp that works with calc-fast.lisp rather than calc.lisp. See the code for an example of how to introduce (random) numerical constants in evolving programs. Notes: - To run this you must: - load the lgp system - load calc-fast.lisp - load this file - evaluate (evolve) - This is set up to solve symbolic regression problems with the stack-based calculator in calc-fast.lisp. - Fitness cases (data for the regression) are listed in *calculator-fitness-cases* (see below). - Fitness values are of the form (error program-length). |# (defparameter *random-constant-magnitude-limit* 10 "Defines a limit on the size of random constants that will be generated for inclusion in evolving calc-fast programs. For example if this is 10 then all random constants will be in the range from -10 to 10, exclusive.") (defun plus-or-minus (n) "Returns n, possibly negated." (if (zerop (randint 2)) (- n) n)) (defun random-integer-constant-form () "Returns a form which, when evaluated, pushes a particular integer constant onto the calc-fast stack. The constant is chosen when random-integer-constant-form is called -- from that point onward it is constant." (list 'push-stack (plus-or-minus (randint *random-constant-magnitude-limit*)))) (defun random-float-constant-form () "Returns a form which, when evaluated, pushes a particular floating point constant onto the calc-fast stack. The constant is chosen when random-integer-constant-form is called -- from that point onward it is constant." (list 'push-stack (plus-or-minus (random::random-float *random-constant-magnitude-limit*)))) (setq *gp-params* '((*instruction-generators* ((random-integer-constant-form) (random-float-constant-form) (list 'CALC-+) (list 'CALC--) (list 'CALC-*) (list 'CALC-/) ; (list 'CALC-SQRT) (list 'CALC-CHS) (list 'CALC-SQ) ;(list 'CALC-X^Y) (list 'CALC-ABS) (list 'CALC-PI) ; (list 'CALC-E) ; removing built in stuff that might make it easier to find sin() ; for my example only -- no other reason why they are ; commented out, feel free to include them in your GP run ; -- AER ; (list 'CALC-LN) ; (list 'CALC-SIN) ; (list 'CALC-COS) ; (list 'CALC-TAN) (list 'CALC-1/X) (list 'CALC-A) ; (list 'CALC-B) (list 'CALC-X<->Y) )) (*genetic-operators* ((1/6 reproduction) (1/6 crossover) (1/6 mutation) (1/6 insertion) (1/6 mutant-insertion) (9/60 deletion) ; (1/10 minimization) ; (1/10 pair-minimization) (1/120 immigration) (1/120 emigration))) (*population-size* 100) (*demes* 10) (*selection-tournament-size* 2) (*replacement-tournament-size* 3) (*best-individual* ((999999999999999999 999999999999999999) nil)) (*halting-fitness* (0 20)) ;; fitness lists are (error length) (*fitness-function* calculator-fitness) (*report-every* 1000) (*max-initial-program-length* 5) (*max-program-length* 50) (*random-seeds* (89942 1079)) (*lexicographic-fitness-epsilon* 0.001) (*initialization-forms* nil) )) #| (defparameter *calculator-fitness-cases* ;; each case is of the form (A B Answer) '( (0.0 0 0.0) (0.05 0 0.007853981633974483) (0.1 0 0.031415926535897934) (0.15 0 0.07068583470577035) (0.2 0 0.12566370614359174) (0.25 0 0.19634954084936207) (0.3 0 0.2827433388230814) (0.35 0 0.38484510006474965) (0.4 0 0.5026548245743669) (0.45 0 0.6361725123519332) (0.5 0 0.7853981633974483) (0.55 0 0.9503317777109126) (0.6 0 1.1309733552923256) (0.65 0 1.3273228961416876) (0.7 0 1.5393804002589986) (0.75 0 1.7671458676442586) (0.8 0 2.0106192982974678) (0.85 0 2.2698006922186256) (0.9 0 2.5446900494077327) (0.95 0 2.8352873698647882) )) |# ; data set for finding sin with simple building blocks -- AER (defparameter *calculator-fitness-cases* ;; each case is of the form (A B Answer) sin a = answer '( (0 0 0.0) (1 0 0.01745240643728351) (2 0 0.03489949670250097) (3 0 0.05233595624294383) (4 0 0.0697564737441253) (5 0 0.08715574274765817) (6 0 0.10452846326765347) (7 0 0.12186934340514749) (8 0 0.13917310096006544) (9 0 0.15643446504023087) (10 0 0.17364817766693036) (11 0 0.1908089953765448) (12 0 0.20791169081775934) (13 0 0.22495105434386498) (14 0 0.24192189559966773) (15 0 0.25881904510252074) (16 0 0.2756373558169992) (17 0 0.2923717047227367) (18 0 0.30901699437494745) (19 0 0.3255681544571567) (20 0 0.3420201433256687) (21 0 0.35836794954530027) (22 0 0.374606593415912) (23 0 0.3907311284892737) (24 0 0.4067366430758002) (25 0 0.42261826174069944) (26 0 0.4383711467890774) (27 0 0.4539904997395468) (28 0 0.4694715627858908) (29 0 0.48480962024633706) (30 0 0.5) (31 0 0.5150380749100542) (32 0 0.529919264233205) (33 0 0.544639035015027) (34 0 0.5591929034707468) (35 0 0.573576436351046) (36 0 0.5877852522924731) (37 0 0.6018150231520483) (38 0 0.6156614753256583) (39 0 0.6293203910498375) (40 0 0.6427876096865394) (41 0 0.6560590289905073) (42 0 0.6691306063588582) (43 0 0.6819983600624986) (44 0 0.6946583704589973) (45 0 0.7071067811865476) (46 0 0.7193398003386511) (47 0 0.7313537016191706) (48 0 0.7431448254773942) (49 0 0.7547095802227719) (50 0 0.766044443118978) (51 0 0.7771459614569708) (52 0 0.7880107536067219) (53 0 0.7986355100472928) (54 0 0.8090169943749475) (55 0 0.8191520442889918) (56 0 0.8290375725550417) (57 0 0.8386705679454239) (58 0 0.848048096156426) (59 0 0.8571673007021122) (60 0 0.8660254037844386) (61 0 0.8746197071393957) (62 0 0.8829475928589269) (63 0 0.8910065241883678) (64 0 0.898794046299167) (65 0 0.9063077870366499) (66 0 0.9135454576426009) (67 0 0.9205048534524404) (68 0 0.9271838545667873) (69 0 0.9335804264972017) (70 0 0.9396926207859083) (71 0 0.9455185755993167) (72 0 0.9510565162951535) (73 0 0.9563047559630354) (74 0 0.9612616959383189) (75 0 0.9659258262890683) (76 0 0.9702957262759965) (77 0 0.9743700647852352) (78 0 0.9781476007338057) (79 0 0.981627183447664) (80 0 0.984807753012208) (81 0 0.9876883405951378) (82 0 0.9902680687415704) (83 0 0.992546151641322) (84 0 0.9945218953682733) (85 0 0.9961946980917455) (86 0 0.9975640502598242) (87 0 0.9986295347545738) (88 0 0.9993908270190958) (89 0 0.9998476951563913) (90 0 1.0) )) (defun calculator-fitness (program) "Evaluates the fitness of the given calculator program using *calculator-fitness-cases*" (let ((total-error 0) (crashed nil) (huge-num 9999999999999999999999)) (dolist (case *calculator-fitness-cases*) (calc-clear) (calc-set-a (first case)) (calc-set-b (second case)) (when (null (ignore-errors (progn (execute-program program) t))) (setq crashed t)) (let ((result (if crashed 0 (min (calc-answer) huge-num)))) (incf total-error (abs (- result (third case)))))) (list (if crashed huge-num total-error) (length program)))) (defun calculator-fitness-computed-if-necessary (individual) "If the fitness of the individual has already been computed then this just returns it. Otherwise calculator-fitness is called on the individual's program and the result is returned." (if (eq (first individual) :no-computed-fitness) (calculator-fitness (second individual)) (first individual))) ;; (evolve)