Reputation: 648
I'm new to Clojure and I tried to implement a genetic algorithm. Thus, I've got a problem, the implementation keeps throwing the following error:
class clojure.lang.LazySeq cannot be cast to class clojure.lang.Associative (clojure.lang.LazySeq and clojure.lang.Associative are in unnamed module
of loader 'app')
To be mentioned that each function is tested individually in REPL and returns the correct results but after putting them all together, the error is thrown, and I don't understand it since it doesn't seems to specify a line number.
The version of clojure is the one from master branch and built with maven, on Windows.
The full code:
(ns ga)
(defn new-individual
[genome-length]
{:genome (vec (repeatedly genome-length #(rand-int 2))) :fitness 0}
)
(defn fitness-function
[genome, target]
(Math/abs (- (reduce + genome) target))
)
(defn calculate-fitness
[population, target]
(defn fitness-function-helper
[individual, target]
(assoc individual :fitness (fitness-function (individual :genome) target))
)
(map (fn [individual] (#(fitness-function-helper individual target))) population)
)
(defn crossover
[first-individual, second-individual, crossover-rate, target]
(let [new-genome (map (fn [i1,i2] (let [crossover-probability (rand)]
(cond
(<= crossover-probability crossover-rate) i1
:else i2
)
)
)
(first-individual :genome) (second-individual :genome)
)]
{:genome new-genome :fitness (fitness-function new-genome target)}
)
)
(defn mutate
[individual, genome-length, target]
(let [new-genome (assoc (individual :genome) (rand-int genome-length) (rand-int 2))]
{:genome new-genome :fitness (fitness-function new-genome target)}
)
)
(defn better
[i1 i2]
(< (i1 :fitness) (i2 :fitness)))
(defn tournament-selection
[population, population-size, steps, tournament-size, new-population, target]
(if (< steps tournament-size)
(recur population population-size (inc steps) tournament-size (conj new-population (nth population ((comp rand-int -) population-size 2))) target
)
;(println new-population)
(first (sort better (calculate-fitness new-population target)))
)
)
(defn new-generation [population, population-size, crossover-rate, target, tournament-size]
(loop [steps 0 new-population ()]
(if (< steps population-size)
(let [i1 (tournament-selection population population-size 0 tournament-size () target)]
(let [i2 (tournament-selection population population-size 0 tournament-size () target)]
(let [offs (crossover i1 i2 crossover-rate target)]
(recur (inc steps) (conj new-population offs))
)
)
)
new-population
)
)
)
(defn new-mutated-generation [population, population-size, genome-length, target]
(loop [steps 0 new-population ()]
(if (< steps population-size)
(recur (inc steps) (conj new-population (mutate (nth population steps) genome-length target)))
new-population
)
)
)
(defn evolve [population-size, genome-length, target]
(let [population (calculate-fitness (repeatedly population-size #(new-individual genome-length)) target)]
(let [offsprings (new-generation population population-size 0.5 target 5)]
(println (new-mutated-generation offsprings population-size genome-length target))
)
)
)
(evolve 10 5 5)
Upvotes: 0
Views: 1123
Reputation: 4901
A stacktrace reveals that the problematic code is the line
(let [new-genome (assoc (vec (individual :genome)) (rand-int genome-length) (rand-int 2))]
and more specifically, the call to assoc
. If we edit the code by inserting the following line just above:
(println "Individual: " (individual :genome) ", " (class (individual :genome)))
it prints out
Individual: (0 1 1 0 1) , clojure.lang.LazySeq
The problem is that assoc
cannot be used with lazy sequences (clojure.lang.LazySeq
) because it does not implement the clojure.lang.Associative interface which is needed by assoc.
This lazy sequence is constructed by the call to map
on this line:
(let [new-genome (map (fn [i1,i2] (let [crossover-probability (rand)]
If you replace map by mapv so that the code looks like this
(let [new-genome (mapv (fn [i1,i2] (let [crossover-probability (rand)]
the code will work.
Upvotes: 2
Reputation: 7576
Error happens in the function mutate
. It has this source:
(defn mutate
[individual, genome-length, target]
(let [new-genome (assoc (individual :genome) (rand-int genome-length) (rand-int 2))]
{:genome new-genome :fitness (fitness-function new-genome target)}))
In one step, you are calling it with these arguments: {:genome (0 1 1 1 1), :fitness 1} 5 5
(genome can have different value, but it's always sequence of numbers).
(individual :genome)
returns (0 1 1 1 1)
(sequence) and then you used assoc
, which is function for hash maps or vectors.
Genome is vector at the beginning, but it's converted into sequence in crossover
function- use mapv
instead of map
here:
(defn crossover
[first-individual, second-individual, crossover-rate, target]
(let [new-genome (mapv (fn [i1, i2] (let [crossover-probability (rand)]
(if (<= crossover-probability crossover-rate) i1 i2)))
(first-individual :genome) (second-individual :genome))]
{:genome new-genome :fitness (fitness-function new-genome target)}))
By the way, all parentheses at the end of definition belong at the same line.
Upvotes: 2