Functional Reactive Programming [39,40] was created to enable the implementa-tion of programs whose behaviour varied with respect to time and unique events.
It has been used to express computations in the fields of functional user inter-faces, animation and simulation. FRP models systems as collections of continu-ous behaviours2, which are implemented as functions from time to values, with the following type (Time is a constant type defined elsewhere in the program);
type Beh a= Time → a
For example, the following is the behaviour of a point in two dimensions ro-tating about the origin. In this example “time” is used directly as the angle in radians, and the point is defined in terms of its distance from the origin.
rotatingPoint:: Double→ Beh (Double, Double) rotatingPoint dist t= (dist ∗ cos t, dist ∗ sin t)
It is then possible to create a range of higher order functions to create new behaviours in terms of existing code. For example mapB operates like map, trans-forming every output of a behaviour by some basic function, and changeTime allows a behaviour to be sped up or slowed down by some transformation of time.
A more detailed treatment of this subject can be found in Chapters 13 and 15 of [39].
changeTime::(Time → Time) → Beh a → Beh a changeTime p b= b ◦ p
mapB::(a → b) → Beh a → Beh b mapB f b= f ◦ b
2This Thesis will ignore events, which while important in FRP do not aid in the implementation of metaheuristics.
4.3.1 FRP for Metaheuristics
FRP is relevant because metaheuristics often make use of self-contained func-tional properties that vary over the lifetime of the program. A strong analogy can also be seen between simulations and metaheuristics, most obvious in Sim-ulated Annealing which was developed from models of physical systems. For example, Simulated Annealing makes use of a temperature strategy, which can be implemented as a function which varies the temperature of the system with respect to the step of the process. This function is usually defined as a geometric progression, or transformation from one state to another, but can be redefined as a behaviour, shown here mathematically with two externally defined constants.
t(i) = tempSeed ∗ geoDropi
Inductive behaviours are also possible, where each new value is based upon the preceding value, back to an initial seed. The use of an inductive behaviour is illustrated in the following reimplementation of the temperature function (where iis time or iteration). In general, most behaviours used for implementing meta-heuristics can only be implemented in this inductive style, with seed data and chained data dependencies.
t(i) =
( tempSeed if i 6 0 t(i − 1) ∗ geoDrop otherwise
FRP is designed for a continuous model of time, whereas in general meta-heuristics will operate largely in an inductive form over discrete steps and so a simpler model of time (such as integers) can be used for this specific purpose.
The choice of the type for time does not impact upon the rest of the implementa-tion.
Behaviours can provide modularity through the clean separation of the various constituent parts which result from the decomposition of the monolithic meta-heuristic. For example, consider this mathematical model of simulated anneal-ing3.
3In this example the condition accepted is a place holder for the functionality of the standard simulated annealing acceptance function, ignored here to reduce complexity. In general this would provide further scope for modularity and tuning.
58
sa(i − 1) if not accepted(i) p(i − 1) otherwise
accepted(i) = e
energy(sa(i−1))−energy(p(i−1))
t(i) > r′(i)
To change the cooling strategy it is only necessary to modify function t; the other functions and the more general pattern of the simulated annealing algorithm remain unchanged. Similarly the perturbation behaviour p, which is a simple function defined over two behaviours, could be more complex, taking into ac-count other details such as history of the process, and implementing this would not change the code for the sa function.
A naive implementation of these functions results in a recursive explosion of computation for higher values of time, an inefficiency due to the recomputation of intermediate values [19]. Memoization of these intermediate values can be used to fix this, however this results in space leaks as all previous values are pre-served. Ideally we wish to allow sharing of only required previous results between behaviours, with intermediate values being cleaned up once they are no longer re-quired. Research into these issues is ongoing [20] (see also the Reactive library4).
4.3.2 Threading Behaviour
The traditional approach to FRP provides a mechanism that allows for an efficient execution of iterative behaviour [19] under some circumstances, presented here in a simplified form. The approach taken is to change the definition of a behaviour to a function from a time to both a value and a new behaviour. This technique is possible if it is known that the new behaviour will only be sampled at times later than the previous sampling point, a promise that is satisfied in the case of iterative algorithms. The simplified type is presented below.
newtype Beh2 a= Beh2 (Time → (a, Beh2 a))
4Haskell library available on Hackage, Conal Elliott, 2010, http://hackage.haskell.org/package/reactive.
The use of this form of continuation generation can allow the efficient sam-pling or unfolding of a behaviour into a list of values, using a function like the following.
unfoldB:: Beh2 a→ Time → [a]
unfoldB(Beh2 f ) startTime = let (v, b) = f startTime
in v: unfoldB b(startTime + 1)
4.3.3 The problem of shared iterative behaviour
The use of continuations for behaviours resolves many issues of efficiency in in-ductive functions, where they are sampled sequentially. However where mutually evolving relationships exist between two or more behaviours then a new issue arises of how to share the results of continuations as each behaviour is sampled5.
For example, where SA is adaptive the temperature behaviour is dependent upon older solutions which have been found, while the solutions are themselves dependent upon older temperatures. Implementing this in terms of evolving be-haviours as seen in section4.3.2will still cause the space and time leaks that the evolving behaviours were introduced to avoid. This may be resolved by explicitly defining behaviours that operate over the related components, however this loses the clean modularity that made FRP attractive, for example:
adaptiveSA:: Beh2(Solution, Temperature)
4.3.4 Impure short term memoization
An alternative, that would provide a cleaner interface, would be a form of function memoization, where the memoizing function is impure, maintaining only a limited number of steps using internal state. While impure this memoization function would still provide a pure function as its result. This would have a form like this:
impureMemo:: Int→ (a → b) → a → b
This could then be used to memoize an iterative temperature function in the fol-lowing way:
5The issue being described can be avoided where two behaviours depend exclusively upon one another’s previous values, or when a single behaviour depends only on its own previous val-ues. The issue appealatex rs when behaviours depend both upon their own histories and other behaviours histories, however this is a rather common condition in metaheuristic algorithms.
60
temp:: Time→ Temperature
temp= impureMemo 3 t -- the parameter 3 is used here as an example -- of a limited number of steps to memoize over This short-term memoization of the values of behaviours would require that the programmer obeys the conventions that behaviours are always sampled sequen-tially, and only be sampled within the specified time window. It would however allow efficient access to and sharing of values within the window.
This concept for short-term memoization would result in only a small code overhead, but is not currently supported. It is possible it would only be of use for this project at the present time, explaining the lack of support. This Thesis did not attempt to resolve this issue, because a more natural approach was conceived while examining FRP.