Skip to content

Commit dffb31d

Browse files
committed
first cut at simulated annealing layout
1 parent 21b7929 commit dffb31d

File tree

2 files changed

+138
-0
lines changed

2 files changed

+138
-0
lines changed
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
; Copyright (c) Rich Hickey. All rights reserved.
2+
; The use and distribution terms for this software are covered by the
3+
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
4+
; which can be found in the file epl-v10.html at the root of this distribution.
5+
; By using this software in any fashion, you are agreeing to be bound by
6+
; the terms of this license.
7+
;; You must not remove this notice, or any other, from this software.
8+
9+
(ns twitterbuzz.anneal)
10+
11+
(defn exp [x]
12+
(js* "Math.exp(~{x})"))
13+
14+
(defn abs [x]
15+
(js* "Math.abs(~{x})"))
16+
17+
(defn random []
18+
(js* "Math.random()"))
19+
20+
(defn standard-prob [e e1 temp]
21+
(if (< e1 e)
22+
1
23+
(exp (/ (- e e1) temp))))
24+
25+
(defn linear-cooling [steps]
26+
(let [min (- 1 (/ steps (dec steps)))]
27+
(fn [t]
28+
(max min (- 1 (/ t steps))))))
29+
30+
(defn anneal
31+
"Given an energy scoring function, a temperature function
32+
(calculates temp given iteration t), a permutation function (creates
33+
a candidate next state given a current state and iteration t), and
34+
an acceptance probability function (of last next energy and temp),
35+
yields a lazy seq of (accepted?) states of the form
36+
{:state s :score :best best :best-score :t t}"
37+
38+
[energy ;;(energy state) -> score
39+
temp ;;(temp t) -> 0-1.0
40+
permute ;;(permute state t) -> new-state
41+
prob ;;(prob e e1 temp) -> 0-1.0
42+
state]
43+
44+
(let [init state
45+
init-score (energy state)
46+
step (fn step [{:keys [state score best best-score t]:as ret}]
47+
(loop [next (permute state) t (inc t)]
48+
(let [next-score (energy next)]
49+
(if (> (prob score next-score (temp t)) (random))
50+
(let [ret {:state next :score next-score :t t
51+
:best (if (< next-score best-score) next best)
52+
:best-score (min next-score best-score)}]
53+
(lazy-seq (cons ret (step ret))))
54+
(recur (permute state) (inc t))))))]
55+
(step {:state init :score init-score :best init :best-score init-score :t 0})))
56+
57+
58+
(comment
59+
60+
(take 10 (take-nth 100
61+
(anneal #(abs (- % 42))
62+
(linear-cooling 1000)
63+
(fn [s _] (+ s (- (random) 0.5)))
64+
standard-prob
65+
55)))
66+
)
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
; Copyright (c) Rich Hickey. All rights reserved.
2+
; The use and distribution terms for this software are covered by the
3+
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
4+
; which can be found in the file epl-v10.html at the root of this distribution.
5+
; By using this software in any fashion, you are agreeing to be bound by
6+
; the terms of this license.
7+
;; You must not remove this notice, or any other, from this software.
8+
9+
(ns twitterbuzz.layout
10+
(:require [twitterbuzz.anneal :as ann]
11+
[goog.math :as math]))
12+
13+
(defn random-loc []
14+
{:x (ann/random) :y (ann/random)})
15+
16+
(defn sqr [x]
17+
(* x x))
18+
19+
(defn sqrt [x]
20+
(js* "Math.sqrt(~{x})"))
21+
22+
(defn dist [{x1 :x y1 :y} {x2 :x y2 :y}]
23+
(sqrt (+ (sqr (- x2 x1)) (sqr (- y2 y1)))))
24+
25+
(defn init-state [mentions-data]
26+
{:locs (zipmap (keys mentions-data) (repeatedly #(random-loc)))
27+
:mentions mentions-data})
28+
29+
(defn score [{:keys [locs mentions]}]
30+
(let [metric (fn [d w] (sqr (- 1 (* d w))))
31+
score-user (fn [[k {:keys [mentions]}]]
32+
(if (zero? (count mentions))
33+
0
34+
(let [loc (locs k)]
35+
(reduce (fn [score [ok w]]
36+
(+ score (metric (dist loc (locs ok)) w)))
37+
0
38+
mentions))))]
39+
(reduce + (map score-user mentions))))
40+
41+
(defn permute-swap [{:keys [locs mentions]} t]
42+
;;first cut - swap
43+
(let [xys (vec (vals locs))
44+
swap1 (math/randomInt (count xys))
45+
swap2 (math/randomInt (count xys))
46+
temp (xys swap1)
47+
xys (assoc xys swap1 (xys swap2))
48+
xys (assoc xys swap2 temp)]
49+
{:locs (zipmap (keys locs) xys)
50+
:mentions mentions}))
51+
52+
(defn permute-move [{:keys [locs mentions]} t]
53+
(let [adj #(min 1.0 (max 0 (+ % (- (* (ann/random) 0.2) 0.1))))
54+
move (fn [{:keys [x y]}]
55+
{:x (adj x) :y (adj y)})
56+
xys (vec (vals locs))]
57+
{:locs (zipmap (keys locs) (map move (vals locs)))
58+
:mentions mentions}))
59+
60+
(comment
61+
;;(def test-data paste data from file here)
62+
63+
(def init (init-state test-data))
64+
65+
(map (fn [x] {:best-score (:best-score x) :t (:t x)})
66+
(take 10 (take-nth 100
67+
(ann/anneal score
68+
(ann/linear-cooling 1000)
69+
permute-move
70+
ann/standard-prob
71+
init))))
72+
)

0 commit comments

Comments
 (0)