Skip to content

Commit 8134ef7

Browse files
committed
adding SimulatedAnnealingSearch
1 parent 3e01b28 commit 8134ef7

File tree

1 file changed

+98
-0
lines changed

1 file changed

+98
-0
lines changed
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package aima.core.search.local
2+
3+
import aima.core.search.{Problem, State}
4+
5+
import scala.annotation.tailrec
6+
import scala.util.Random
7+
8+
/**
9+
* <pre>
10+
* function SIMULATED-ANNEALING(problem, schedule) returns a solution state
11+
* inputs: problem, a problem
12+
* schedule, a mapping from time to "temperature"
13+
*
14+
* current &larr; MAKE-NODE(problem.INITIAL-STATE)
15+
* for t = 1 to &infin; do
16+
* T &larr; schedule(t)
17+
* if T = 0 then return current
18+
* next &larr; a randomly selected successor of current
19+
* &Delta;E &larr; next.VALUE - current.value
20+
* if &Delta;E &gt; 0 then current &larr; next
21+
* else current &larr; next only with probability e<sup>&Delta;E/T</sup>
22+
* </pre>
23+
*
24+
* @author Shawn Garner
25+
*/
26+
object SimulatedAnnealingSearch {
27+
28+
type Schedule = Int => Double
29+
30+
object BasicSchedule {
31+
32+
val k: Int = 20
33+
val lam: Double = 0.045
34+
val limit: Int = 100
35+
36+
val schedule: Schedule = { t: Int => // time steps 1 to infinity (Integer.Max)
37+
if (t < limit) {
38+
k * math.exp((-1) * lam * t)
39+
} else {
40+
0.0
41+
}
42+
}
43+
}
44+
45+
final case class StateValueNode(state: State, value: Double) extends State
46+
47+
def apply(stateToValue: State => Double, problem: Problem): State =
48+
apply(stateToValue, problem, BasicSchedule.schedule)
49+
50+
def apply(stateToValue: State => Double, problem: Problem, sched: Schedule): State = {
51+
val random = new Random()
52+
53+
def makeNode(state: State): StateValueNode = StateValueNode(state, stateToValue(state))
54+
55+
def schedule(t: Int): Double = {
56+
val T = sched(t)
57+
if (T < 0.0d) {
58+
throw new IllegalArgumentException("Configured schedule returns negative temperatures: t=" + t + ", T=" + T) // TODO: we don't throw in Scala
59+
}
60+
T
61+
}
62+
63+
def randomlySelectSuccessor(current: StateValueNode): StateValueNode = {
64+
// Default successor to current, so that in the case we reach a dead-end
65+
// state i.e. one without reversible actions we will return something.
66+
// This will not break the code above as the loop will exit when the
67+
// temperature winds down to 0.
68+
val actions = problem.actions(current.state)
69+
val successor = {
70+
if (actions.nonEmpty) {
71+
makeNode(problem.result(current.state, actions(random.nextInt(actions.size))))
72+
} else {
73+
current
74+
}
75+
}
76+
77+
successor
78+
}
79+
80+
@tailrec def recurse(current: StateValueNode, t: Int): State = {
81+
val T = schedule(t)
82+
if (T == 0.0d) {
83+
current
84+
} else {
85+
val next = randomlySelectSuccessor(current)
86+
val DeltaE = next.value - current.value
87+
lazy val acceptDownHillMove = math.exp(DeltaE / T) > random.nextDouble()
88+
if (DeltaE > 0.0d || acceptDownHillMove) {
89+
recurse(next, t + 1)
90+
} else {
91+
recurse(current, t + 1)
92+
}
93+
}
94+
}
95+
96+
recurse(makeNode(problem.initialState), 1)
97+
}
98+
}

0 commit comments

Comments
 (0)