Skip to content

Commit 54d8ee8

Browse files
authored
Add files via upload
1 parent 9af1a71 commit 54d8ee8

File tree

12 files changed

+6615
-0
lines changed

12 files changed

+6615
-0
lines changed

util/README.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
This is the stub README.txt for the "util" project.

util/art.lisp

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
;;;; art.lisp
2+
3+
(in-package #:util)
4+
5+
;; do not use defparameter,defconstant,defvar, and try to copy-table, they will affect each other
6+
(defun make-art ;;each sample should be the same length. e.g. [0.3,0.5,0.1,0.9] for 4 features
7+
(&key
8+
(max-num-categories nil)
9+
(vigilance 0.75)
10+
(learning-rate 0.2)
11+
(complement-code t))
12+
(>>
13+
'max-num-categories max-num-categories
14+
'vigilance vigilance
15+
'learning-rate learning-rate
16+
'complement-code complement-code
17+
'nodes (>>) ;;prototypes eg,a prototype [0.3,0.5,0.1,0.9] for 4 features, like nodes in SOM
18+
))
19+
20+
;; complement code pairs [n, 1 - n, ...] e.g. [0.2, 0.3] -> [0.2,0.8, 0.3,0.7]
21+
(defun complement-code (&key (data '(0.2 0.3)))
22+
(flatten (mapcar #'(lambda (x) (list x (- 1 x))) data)))
23+
24+
;;[1,2,3],[4,5,6] -> 1*4+2*5+3*6 -> 32
25+
(defun inner (&key (a '(1 2 3)) (b '(4 5 6)))
26+
(apply '+ (mapcar #'(lambda (x y) (* x y)) a b)))
27+
28+
;;similarity: greater the result is, more similar they are. [1,5,3],[6,3,2]->(1+3+2)/(1+5+3)=2/3
29+
(defun fuzzy-and (&key (a '(1 5 3)) (b '(6 3 2)))
30+
(let ((sum-a (apply '+ a)))
31+
(if (= 0 sum-a)
32+
0
33+
(/ (apply '+ (mapcar #'(lambda (x y) (min x y)) a b)) sum-a))))
34+
35+
(defun update-art-node (&key (art (make-art)) (sample '(0.2 0.3 0.1 0.6)) (node-key 0))
36+
;;move art-node towards sample, low learning-rate -> small move
37+
(when (>> art 'complement-code) (setf sample (complement-code :data sample)))
38+
(>> art `(nodes ,node-key)
39+
(mapcar #'(lambda (x y) (+ y (* (>> art 'learning-rate) (- x y)))) sample (>> art `(nodes ,node-key)))))
40+
41+
(defun categorize-art (&key (art (make-art)) (sample '(0.2 0.3 0.1 0.6)))
42+
(when (not (equalp (>>) (>> art 'nodes)));;existing categories
43+
(let ((candi-keys '()) (similarities '())
44+
(sample1 (if (>> art 'complement-code) (complement-code :data sample) sample)))
45+
(dolist (i (hash-keys (>> art 'nodes)))
46+
(let ((similarity (fuzzy-and :a sample1 :b (>> art `(nodes ,i)))))
47+
(when (> similarity (>> art 'vigilance))
48+
(push i candi-keys)
49+
(push similarity similarities))))
50+
(when candi-keys
51+
(let ((key (nth (position (apply 'max similarities) similarities) candi-keys)))
52+
(update-art-node :art art :sample sample :node-key key)
53+
(return-from categorize-art key))))) ;;return the key of art-node
54+
nil)
55+
56+
(defun add-new-art-category (&key (art (make-art)) (sample '(0.2 0.3 0.1 0.6)))
57+
(let ((key (if (= 0 (hash-table-count (>> art 'nodes))) 0 (+ 1 (apply 'max (hash-keys (>> art 'nodes))))))
58+
(sample1 (if (>> art 'complement-code) (complement-code :data sample) sample)))
59+
(>> art `(nodes ,key) sample1)
60+
key)) ;;return the key of new art-node
61+
62+
(defun step-art (&key (art (make-art)) (sample '(0.2 0.3 0.1 0.6)))
63+
(let ((category (categorize-art :art art :sample sample)))
64+
(when (null category)
65+
(let ((max-num-cates (>> art 'max-num-categories)))
66+
(when (not (and max-num-cates (= (hash-table-count (>> art 'nodes)) max-num-cates))) ;;existing space for new category
67+
(setf category (add-new-art-category :art art :sample sample)))))
68+
category)) ;;return the key of art-node
69+
70+
;;e.g. data=[[0.2,0.3,0.1,0.6],[0.1,0.7,0.3,0.5]] are samples
71+
;;return categories eg,[0, 0, 1, 0, 1, 2] if len(data)==6
72+
(defun train-art (&key (art (make-art)) (data '((0.2 0.3 0.1 0.6)(0.1 0.7 0.3 0.5))))
73+
(mapcar #'(lambda (x) (step-art :art art :sample x)) data))
74+
75+
(defun test-art
76+
(&key (data-train '((1 1 1 1 1 0 0 0 0 0 0 0)
77+
(1 1 1 0 1 0 0 0 0 0 0 0)
78+
(0 0 0 0 0 0 0 1 1 1 1 1)
79+
(1 1 0 1 1 0 0 0 0 0 0 0)
80+
(0 0 0 0 0 0 0 1 1 1 1 0)
81+
(0 1 0 1 0 1 0 1 0 1 0 1)))
82+
(data-test '((1 1 1 0 1 0 0 0 0 0 0 0)
83+
(0 0 0 0 0 0 0 1 1 0 1 0)
84+
(0 0 0 0 0 1 1 0 0 0 0 0)
85+
(0 0 0 0 0 0 0 0.6 0.6 0.6 0.6 0))))
86+
(let* ((net (make-art))
87+
(train-result (train-art :art net :data data-train)))
88+
(show "Training results: {}" train-result)
89+
(dolist (da data-test)
90+
(print (categorize-art :art net :sample da)))))
91+
92+
;;(test-art :data-train '((0.8 0.1 0.7 0.9) (0.1 0.7 0.3 0.5)) :data-test '((0.2 0.6 0.4 0.4) (0.7 0.2 0.6 0.8)))

util/gng.lisp

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
;;;; gng.lisp ;growing neural gas
2+
3+
(in-package #:util)
4+
5+
;; do not use defparameter,defconstant,defvar, and try to copy-table, they will affect each other
6+
(defun make-gng
7+
(&key
8+
(input-dim 2)
9+
(max-nodes 2147483647)
10+
)
11+
(>>
12+
'input-dim input-dim
13+
'max-nodes max-nodes
14+
'eps-b 0.2 ;;the rate of moving closest node to sample
15+
'eps-n 0.006 ;;the rate of moving neighbors of closest node to sample
16+
'max-age 50
17+
'lambd 100
18+
'alpha 0.5
19+
'd 0.995
20+
'time-len 0
21+
'nodes (>>)
22+
'edges (>>)))
23+
24+
(defun add-gng-node (&key (gng (make-gng)) (pos '(0 0)) (cum-error 0.0))
25+
(let ((key (if (= 0 (hash-table-count (>> gng 'nodes))) 0 (+ 1 (reduce #'max (hash-keys (>> gng 'nodes)))))))
26+
(>> gng `(nodes ,key) (>> 'pos pos 'cum-error cum-error))
27+
key))
28+
29+
(defun add-gng-edge (&key (gng (make-gng)) (nodes '(0 1)) (age 0))
30+
(let ((key (if (= 0 (hash-table-count (>> gng 'edges))) 0 (+ 1 (reduce #'max (hash-keys (>> gng 'edges)))))))
31+
(>> gng `(edges ,key) (>> 'nodes nodes 'age age))
32+
key))
33+
34+
(defun get-nearest-gng-nodes (&key (gng (make-gng)) (vector '(0 0)))
35+
;;Return the two nodes in the graph that are nearest to vector and their squared distances. return (list (dist0 node0) (dist1 node1))
36+
(let ((distances (mapcar #'(lambda (x) (list (squared-distance :v0 vector :v1 (>> gng `(nodes ,x pos))) x)) (hash-keys (>> gng 'nodes)))))
37+
(setf distances (sort (copy-seq distances) #'list<))
38+
(list (car distances) (second distances))))
39+
40+
(defun move-gng-node (&key (gng (make-gng)) (node 0) (vector '(0 0)) (eps (>> gng 'eps-b)))
41+
(loop for i in (range (length vector)) do
42+
(let ((pos-i (nth i (>> gng `(nodes ,node pos)))))
43+
(setf (nth i (>> gng `(nodes ,node pos))) (+ pos-i (* eps (- (nth i vector) pos-i)))))))
44+
45+
(defun remove-old-gng-edges (&key (gng (make-gng)) (edges (hash-keys (>> gng 'edges))))
46+
;;Remove all edges older than the maximal age
47+
(loop for k in edges do
48+
(when (> (>> gng `(edges ,k age)) (>> gng 'max-age))
49+
(let ((nodes-now (>> gng `(edges ,k nodes))))
50+
(remhash k (>> gng 'edges))
51+
(loop for node in nodes-now do
52+
(when (= 0 (node-degree gng :node node))
53+
(remhash node (>> gng 'nodes))))))))
54+
55+
(defun insert-new-gng-node (&key (gng (make-gng)))
56+
;;Insert a new node in the graph where it is more necessary i.e. where the error is the largest
57+
(let ((errors (mapcar #'(lambda (x) (list (>> gng `(nodes ,x cum-error)) x)) (hash-keys (>> gng 'nodes))))
58+
(q-node nil)
59+
(neighbor-nodes '())
60+
(neighbor-errors '())
61+
(f-node nil)
62+
)
63+
(setf errors (sort (copy-seq errors) #'list>))
64+
(setf q-node (cadar errors))
65+
(setf neighbor-nodes (neighbors gng :node q-node))
66+
(setf neighbor-errors (mapcar #'(lambda (x) (list (>> gng `(nodes ,x cum-error)) x)) neighbor-nodes))
67+
(setf neighbor-errors (sort (copy-seq neighbor-errors) #'list>))
68+
(setf f-node (cadar neighbor-errors))
69+
;;new node, halfway between the worst node and the worst of its neighbors
70+
(let ((new-node (add-gng-node :gng gng :pos (mapcar #'(lambda (x y) (* 0.5 (+ x y))) (>> gng `(nodes ,q-node pos)) (>> gng `(nodes ,f-node pos))))))
71+
(remhash (car (get-edges gng :nodes (list q-node f-node))) (>> gng 'edges))
72+
(add-gng-edge :gng gng :nodes (list q-node new-node))
73+
(add-gng-edge :gng gng :nodes (list f-node new-node))
74+
(let ((q-error (>> gng `(nodes ,q-node cum-error))))
75+
(>> gng `(nodes ,q-node cum-error) (* q-error (>> gng 'alpha))))
76+
(let ((f-error (>> gng `(nodes ,f-node cum-error))))
77+
(>> gng `(nodes ,f-node cum-error) (* f-error (>> gng 'alpha))))
78+
(>> gng `(nodes ,new-node cum-error) (* 0.5 (+ (>> gng `(nodes ,q-node cum-error)) (>> gng `(nodes ,f-node cum-error))))))))
79+
80+
(defun train-gng (&key (gng (make-gng)) (input '((0 0))) (init-nodes 2))
81+
(when (= 0 (hash-table-count (>> gng 'nodes))) ;if missing, generate two initial nodes at random
82+
(loop for i in (range init-nodes) do
83+
;;(add-gng-node :gng gng :pos (mapcar #'(lambda (x) (normal-random :mean 0.5 :deviation 0.2)) (range (input-dim gng))))
84+
(add-gng-node :gng gng :pos (mapcar (lambda (x) (random-uniform (list (- x 0.01) (+ x 0.01)))) (random-choice input)))))
85+
(dolist (vector input)
86+
(let ((time-len (>> gng 'time-len))) (>> gng 'time-len (+ 1 time-len))) ;; incf time-len
87+
(let* ((nearest-nodes (get-nearest-gng-nodes :gng gng :vector vector)) ;(list (dist0 node0) (dist1 node1))
88+
(node0 (cadr (first nearest-nodes)))
89+
(dist0 (car (first nearest-nodes)))
90+
(edges0 (get-emanating-edges gng :node node0))
91+
(node1 (cadr (second nearest-nodes))))
92+
(dolist (e edges0) ;increase age of the emanating edges of the closest node0
93+
(let ((age-now (>> gng `(edges ,e age))))
94+
(>> gng `(edges ,e age) (+ 1 age-now))))
95+
(let ((c-error (>> gng `(nodes ,node0 cum-error)))) ;update error of the closest node0
96+
(>> gng `(nodes ,node0 cum-error) (+ c-error (sqrt dist0))))
97+
;;move nearest node and neighbours
98+
(move-gng-node :gng gng :node node0 :vector vector :eps (>> gng 'eps-b))
99+
(let ((neighbor-nodes (neighbors gng :node node0)))
100+
(dolist (n neighbor-nodes)
101+
(move-gng-node :gng gng :node n :vector vector :eps (>> gng 'eps-n)))
102+
;;update the edge connecting node0 and node1
103+
(if (member node1 neighbor-nodes :test #'equal)
104+
(let ((edge01 (car (get-edges gng :nodes (list node0 node1)))))
105+
(>> gng `(edges ,edge01 age) 0))
106+
(add-gng-edge :gng gng :nodes (list node0 node1))))
107+
;; remove old edges
108+
(remove-old-gng-edges :gng gng :edges edges0)
109+
;; add a new node each lambd steps
110+
(when (and (= 0 (mod (>> gng 'time-len) (>> gng 'lambd))) (< (hash-table-count (>> gng 'nodes)) (>> gng 'max-nodes)))
111+
(insert-new-gng-node :gng gng))
112+
;; decrease the errors of all nodes
113+
(loop for node in (hash-keys (>> gng 'nodes)) do
114+
(let ((c-error (>> gng `(nodes ,node cum-error))))
115+
(>> gng `(nodes ,node cum-error) (* c-error (>> gng 'd))))))))
116+
117+
(defun circumference-distr (&key (center '(0.0 0.0)) (radius 1.0) (n 2000))
118+
(let* ((phi (mapcar #'(lambda (x) (random (* 2 pi))) (range n))))
119+
(mapcar #'(lambda (x) (list (+ (* radius (cos x)) (car center)) (+ (* radius (sin x)) (second center)))) phi)))
120+
121+
(defun circle-distr (&key (center '(0.0 0.0)) (radius 1.0) (n 2000))
122+
(let* ((phi (mapcar #'(lambda (x) (random (* 2 pi))) (range n)))
123+
(sqrt-r (mapcar #'(lambda (x) (sqrt (random (* radius radius)))) (range n))))
124+
(mapcar #'(lambda (x) (list (+ (* (nth x sqrt-r) (cos (nth x phi))) (car center)) (+ (* (nth x sqrt-r) (sin (nth x phi))) (second center)))) (range n))))
125+
126+
(defun rectangle-distr (&key (center '(0.0 0.0)) (w 2.0) (h 1.0) (n 2000))
127+
(mapcar #'(lambda (x) (list (+ (random-uniform (list (/ w -2) (/ w 2))) (car center))
128+
(+ (random-uniform (list (/ h -2) (/ h 2))) (second center)))) (range n)))
129+
130+
(defun test-gng ()
131+
(let ((g (make-gng :max-nodes 10)))
132+
(show "g: {}" g)
133+
(let ((node (add-gng-node :gng g)))
134+
(format t "node-index: ~a~%" node))
135+
(add-gng-node :gng g :pos '(2 3))
136+
(add-gng-node :gng g :pos '(4 5))
137+
(add-gng-node :gng g :pos (mapcar #'(lambda (x y) (* 0.5 (+ x y))) (>> g `(nodes 1 pos)) (>> g `(nodes 2 pos))))
138+
(show "(>> g 'nodes) {}" (hash2lst (>> g 'nodes)))
139+
(add-gng-edge :gng g)
140+
(add-gng-edge :gng g :nodes '(1 2) :age 100)
141+
(add-gng-edge :gng g :nodes '(3 0) :age 0)
142+
(let ((pair-nodes '(2 1)))
143+
(format t "the edge of pair-nodes ~a is ~a~%" pair-nodes (car (get-edges g :nodes pair-nodes)))
144+
)
145+
(format t "before inserting new node~%")
146+
(show "(>> g 'edges) {}" (hash2lst (>> g 'edges)))
147+
(insert-new-gng-node :gng g)
148+
(format t "after inserting new node~%")
149+
(show "(>> g 'edges) {}" (hash2lst (>> g 'edges)))
150+
(let ((node 1)) (format t "neighbors of node ~a: ~a~%" node (neighbors g :node node)))
151+
(format t "~a~%" (get-nearest-gng-nodes :gng g :vector '(3 5)))
152+
(move-gng-node :gng g :node 2 :vector '(3 5))
153+
(show "(>> g 'nodes) {}" (hash2lst (>> g 'nodes)))
154+
(node-degree g :node 2)
155+
(remove-old-gng-edges :gng g)
156+
(show "(>> g 'edges) {}" (hash2lst (>> g 'edges)))
157+
(let ((lst (hash2lst (>> g 'edges))))
158+
(format t "~a~%" lst)
159+
(format t "~a~%" (getf (getf lst 0) 'to)))
160+
(loop for i in (range 10000) do
161+
(train-gng :gng g :input '((20 20) (25 20) (25 25) (20 25))))
162+
(format t "nodes: ~a~%" (hash2lst (>> g 'nodes)))
163+
(format t "edges: ~a~%" (hash2lst (>> g 'edges)))))
164+
;;(test-gng)
165+
166+
(defun set-samples ()
167+
(let* ((n 1000)
168+
(cf1 (circumference-distr :center '(0.8 0.475) :radius 0.1 :n n))
169+
(cf2 (circumference-distr :center '(0.65 0.4) :radius 0.015 :n n))
170+
(cl1 (circle-distr :center '(0.25 0.65) :radius 0.025 :n (/ n 2)))
171+
(cl2 (circle-distr :center '(0.675 0.625) :radius 0.035 :n n))
172+
(r1 (rectangle-distr :center '(0.425 0.5) :w 0.05 :h 0.2 :n n))
173+
(r2 (rectangle-distr :center '(0.575 0.5) :w 0.05 :h 0.2 :n n))
174+
(r3 (rectangle-distr :center '(0.5 0.575) :w 0.1 :h 0.05 :n (/ n 2)))
175+
(r4 (rectangle-distr :center '(0.5 0.425) :w 0.1 :h 0.05 :n (/ n 2)))
176+
(samples (append cf1 cf2 cl1 cl2 r1 r2 r3 r4)))
177+
(shuffle samples)))
178+
;;(show "{}" (set-samples))
179+
180+
(defun main-loop-gng-test (&key (width 600) (height 300) (timex 2) (samples (set-samples)) (g (make-gng)))
181+
(sdl:with-init ()
182+
(sdl:window width height :flags sdl:sdl-opengl
183+
:title-caption "visual"
184+
:icon-caption "visual")
185+
;;(sdl:window w-width w-height :opengl t :opengl-attributes '((:sdl-gl-depth-size 16)(:sdl-gl-doublebuffer 1)))
186+
(setf (sdl:frame-rate) 0)
187+
(>> g 'max-nodes 1000)
188+
;; load GL extensions
189+
;;(setf cl-opengl-bindings:*gl-get-proc-address* #'sdl-cffi::sdl-gl-get-proc-address)
190+
(sdl:with-events ()
191+
(:quit-event () t)
192+
(:idle ()
193+
(when (< timex 3000) (incf timex))
194+
(train-gng :gng g :input (list (random-choice samples)) :init-nodes 3)
195+
;; keep working with main loop (sbcl) :fd-handler swank:*communication-style*
196+
;;#+(and sbcl (not sb-thread)) (restartable (sb-sys:serve-all-events 0))
197+
(gl:clear-color 1 1 1 1)
198+
(gl:clear :color-buffer :depth-buffer)
199+
(let ((origin (list 50 50)) (width (/ width 3)) (height (/ height 1.5)))
200+
(before-visual-2D :origin origin :width width :height height :color '(0 0 1 1) :border? t)
201+
(visual-samples samples :origin origin :width width :height height :color '(0 0 1 1) :border? t)
202+
(visual-gng g :origin origin :width width :height height :color '(1 0 0 1) :border? nil))
203+
;;(draw-triangle2 :origin (list (/ width 2) 0) :width (/ width 2) :height height)
204+
(let ((origin (list (+ (/ width 2) 10) 10)) (width (- (/ width 2) 20)) (height (- height 20)))
205+
(before-visual-2D :origin origin :width width :height height :color '(0 0 1 1) :border? t)
206+
(draw-curve :origin origin :width width :height height :timex timex))
207+
(gl:flush)
208+
(sdl:update-display)))))
209+
;;(main-loop-gng-test :width 1000 :height 300)
210+
211+
212+
213+
214+
215+

0 commit comments

Comments
 (0)