-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathOptimize.hs
More file actions
116 lines (92 loc) · 2.63 KB
/
Copy pathOptimize.hs
File metadata and controls
116 lines (92 loc) · 2.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
module Optimize where
import Data.List
type Point = [Double]
{-
Typical use of minimize:
goal p . giveUp k . take n . minimize (repeat d) xs $ h
Here:
- p :: a -> Bool
what result value are we looking for
- k :: Int
how many times in a row can the result get worse before we give up
- n :: Int
maximum number of iterations
- d :: Double
size of "the box", the initial jumps that will be taken
- xs :: [Double]
starting point
- h :: [Double] -> a
function to minimize
-}
-- helpers
goal :: (a -> Bool) -> [(Point,a,a)] -> (Point,a)
goal p ((vs,x,_):qs)
| null qs || p x = (vs,x)
| otherwise = goal p qs
goal p [] = error "goal []"
giveUp :: Ord a => Int -> [(Point,a,a)] -> [(Point,a,a)]
giveUp n = go n
where
go 0 _ = []
go k (q@(vs,x,y):qs@((vs',x',y'):_))
| x == x' && y' >= y = q : go (k-1) qs
| otherwise = q : go n qs
go _ qs = qs
-- produces a possibly infinite list of (point,best-result,worst-result)
minimize :: Ord a => Point -> Point -> (Point -> a) -> [(Point,a,a)]
minimize _ [] h = [([],x,x)] where x = h []
minimize box0 p h = go (sort [ pair p | p <- ps0 ])
where
-- trim box
box = take (length p) box0
-- initial points
ps0 =
map (zipWith (+) p) $
map (zipWith (*) box) $
initial (length box)
initial n =
replicate n 0:
[ replicate i b ++ [a] ++ replicate (n-i-1) b
| i <- [0..n-1] ]
where
nf :: Double
nf = fromIntegral n
b = (sqrt(nf+1)-1) / (nf * sqrt 2)
a = b + 1 / sqrt 2
-- pairing up result and point
pair p = (h p, p)
-- refactored from https://en.wikipedia.org/wiki/Nelder-Mead_method
go xps =
(p0,x0,xL) :
if xR < xN then
if x0 <= xR || xR <= xE then
-- reflect
go (insert qR xpsI)
else
-- expand
go (insert qE xpsI)
else
if xC < xL then
-- contract
go (insert qC xpsI)
else
-- shrink
go (sort (q0:[ pair (p -*-> (0.15,p0)) | (_,p) <- tail xps ]))
where
xpsI = init xps
q0@(x0,p0) = head xps
qN@(xN,_) = last xpsI
qL@(xL,pL) = last xps
-- centroid
pO = centroid (map snd xpsI)
-- reflect, expand, contract
qR@(xR,_) = pair (pL -*-> (2, pO))
qE@(xE,_) = pair (pL -*-> (3, pO))
qC@(xC,_) = pair (pL -*-> (0.4, pO)) -- not 0.5 to avoid the same point twice
centroid :: [Point] -> Point
centroid ps = [ sum [p!!i | p <- ps] / fromIntegral l | i <- [0..l-1] ]
where
l = length ps
-- generic "towards": reflect (a=2), expand (a=3), contract (a=0.5), shrink (a=0.1)
(-*->) :: Point -> (Double, Point) -> Point
p -*-> (a,q) = [ x + a*(y - x) | (x,y) <- p `zip` q ]