Type assertion might narrow the types. Because compare is only meaningful between Int, so we can narrow type to Int before comparing. This might be better than invoke <(::Union{Nothing,Int},Union{Nothing,Int}).
Edit: compiler is good at optimizing, I guess compiler can do union splitting on this case, so adding type assertion might not be quite useful here.
Follwing your suggested approach for the cross_parents function did indeed reduced memory allocation to about half but doubled run time. That is why I decided to stick to existing approach.
After follwing most tips performance is amazing 0.926318 seconds (3.88 M allocations: 485.378 MiB, 10.53% gc time)
Here is the most up to date code:
- using Random, CSV, DataFrames
- const file = "21_Points.csv"
-
- const pop_size = 20000
- const elites = 200
- const survivors = 9900
- const mutts = 5000
- const gens = 50
-
- function generate_distance_matrix(file = "21_Points.csv")
- #Importar datos de Excel
0 nodes_import = CSV.read(file, header = true)
-
- #Definir que hay en cada columna del excel y cuantos nodos hay en total
0 num_nodes = size(nodes_import,1)
- idcol = 1
- Xcol = 2
- Ycol = 3
-
- #Generar la Matriz de distancias a partir del numero de nodos
0 distance_matrix = Array{Float64}(undef, (num_nodes, num_nodes))
- #Llenar la matriz de distancias entre todos los puntos usando Pitagoras
0 for n in 1:num_nodes
0 for s in 1:num_nodes
0 distance_matrix[n,s] = sqrt((nodes_import[n,Ycol] - nodes_import[s,Ycol])^2 +
- (nodes_import[n,Xcol] - nodes_import[s,Xcol])^2)
- end
- end
- #println(pretty_table(nodes_import))
- #println(pretty_table(distance_matrix))
0 return distance_matrix
- end
-
- const distance_matrix = generate_distance_matrix("21_Points.csv")
- const num_nodes = size(distance_matrix, 1)
-
- function fitness_function(path)
- return distance_matrix[path[end],path[1]] +
- sum(distance_matrix[path[i-1],path[i]] for i = 2:num_nodes)
- end
-
- struct Individual
- generation::Int
- genes::Vector{Int}
- fitness_score::Float64
- end
- Individual(gen, genes) = Individual(gen, genes, fitness_function(genes))
-
- function init_poulation(pop_size)
160080 population = Vector{Individual}(undef,pop_size)
0 for i in 1:pop_size
6080000 population[i] = Individual(1,randperm(num_nodes))
- end
0 return population
- end
-
- function new_generation!(population, mating_pool, gen)
-
0 for p in 1:survivors
0 parent1 = mating_pool[p]
0 parent2 = mating_pool[rand(1:survivors)]
-
0 for (c,I) in enumerate(cross_parents(parent1.genes, parent2.genes, gen))
0 if c == 1
0 population[elites+p] = I
0 elseif c == 2
0 population[elites+survivors+p] = I
- else
0 print("Error")
- end
- end
- end
-
0 for m in 1:(mutts÷2)
0 mutt_1 = population[rand(10:pop_size)]
0 mutt_2 = population[rand(10:pop_size)]
0 mutate_type1!(mutt_1)
0 mutate_type2!(mutt_2)
- end
- end
-
- function cross_parents(parent1_genes, parent2_genes, gen)
-
0 cross_point = rand(1:num_nodes-1)
-
131947200 ch1_genes = similar(parent1_genes)
131947200 ch2_genes = similar(parent2_genes)
23284800 ch1_genes[1:cross_point] = @view parent1_genes[1:cross_point]
23284800 ch2_genes[1:cross_point] = @view parent2_genes[1:cross_point]
0 parent1_left = @view parent1_genes[cross_point+1:end]
0 parent2_left = @view parent2_genes[cross_point+1:end]
-
130339920 ch1_pending = similar(parent1_left, Tuple{Int, Int})
130339920 ch2_pending = similar(parent2_left, Tuple{Int, Int})
-
-
0 for (index, value) in enumerate(parent1_left)
0 ch1_pending[index] = (value, findfirst(isequal(value), parent2_genes)::Int64)
- end
43035072 sort!(ch1_pending, by = last)
0 for (index, (value, _)) in enumerate(ch1_pending)
0 ch1_genes[cross_point+index] = value
- end
-
0 for (i, v) in enumerate(parent2_left)
0 ch2_pending[i] = (v, findfirst(isequal(v), parent1_genes)::Int64)
- end
43035072 sort!(ch2_pending, by = last)
0 for (i,(v, _)) in enumerate(ch2_pending)
0 ch2_genes[cross_point+i] = v
- end
-
46569600 return (Individual(gen,ch1_genes), Individual(gen,ch2_genes))
- end
-
- function mutate_type1!(mutt_1::Individual)
0 mutt_gene = rand(1:num_nodes-1)
0 i = findfirst(isequal(mutt_gene), mutt_1.genes)
0 j = findfirst(isequal(mutt_gene+1), mutt_1.genes)
0 mutt_1.genes[i] = mutt_gene+1
0 mutt_1.genes[j] = mutt_gene
- end
-
- function mutate_type2!(mutt_2::Individual)
0 mutt_gene = rand(1:num_nodes-2)
0 i = findfirst(isequal(mutt_gene), mutt_2.genes)
0 j = findfirst(isequal(mutt_gene+2), mutt_2.genes)
0 mutt_2.genes[i] = mutt_gene+2
0 mutt_2.genes[j] = mutt_gene
- end
-
- function GA()
2560 population = init_poulation(pop_size)
79312 mating_pool = Array{Individual}(undef, survivors)
-
0 for g = 2:gens
-
0 partialsort!(population, survivors, by = p -> p.fitness_score)
2352 mating_pool = @view population[1:survivors]
-
0 new_generation!(population, mating_pool, g)
-
0 if (population[1].fitness_score == population[elites].fitness_score) || ((g - population[1].generation) > 6)
0 print("Convergencia en la GEN")
0 println(g)
0 print("TODOS ELITES IGUALES")
- break
- end
0 if ((g - population[1].generation) > gens/5)
0 print("Convergencia en la GEN")
0 println(g)
0 print("SIN CAMBIOS en x GENS x = ")
0 println(gens/5)
- break
- end
9968 println(g)
- end
-
80144 sort!(population, by = p -> p.fitness_score)
0 return population[1]
- end
-
0 @time winner = GA()
- print(winner)
-
Huh, that’s very surprising to me
Is the CSV file you’re using publicly available? I’d like to play around with it a little, if that’s possible.
This are the points I was using (they are actually 23
haha) They are random coordinates between 1 and 100.
ID,X coordinate,Y coordiante
1,64,74
2,53,63
3,53,20
4,98,53
5,90,97
6,43,44
7,48,46
8,87,55
9,58,45
10,40,82
11,43,13
12,23,74
13,12,80
14,65,7
15,64,99
16,11,74
17,55,98
18,65,99
19,92,17
20,96,11
21,67,68
22,24,79
23,17,32
Additionally, Julia arrays are column-major, so iterating the first dimension should take place in the innermost loop:
for s in 1:num_nodes
for n in 1:num_nodes
distance_matrix[n,s] = sqrt((nodes_import[n,Ycol] - nodes_import[s,Ycol])^2 +
(nodes_import[n,Xcol] - nodes_import[s,Xcol])^2)
end
end