From f59500632d93778aeef0418ca7caa4e50cf5ae8d Mon Sep 17 00:00:00 2001 From: Nikola-Mircic Date: Thu, 4 Jan 2024 22:57:48 +0100 Subject: [PATCH 1/7] Test for Genetic Algorithm --- test/genetic_algorithm.jl | 29 +++++++++++++++++++++++++++++ test/runtests.jl | 3 +-- 2 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 test/genetic_algorithm.jl diff --git a/test/genetic_algorithm.jl b/test/genetic_algorithm.jl new file mode 100644 index 0000000..09661bf --- /dev/null +++ b/test/genetic_algorithm.jl @@ -0,0 +1,29 @@ +using GeneticAlgorithm + +@testset "GeneticAlgorithm" begin + #= + | Find four numbers that add up to 143. + | + | x1 + x2 + x3 + x4 = 143 + | x1=?, x2=?, x3=?, x4=? + =# + + populationSize = 100 + genesLength = 4 + minGene = 0 + maxGene = 143 + + elitePercent = 0.15 + mutationPercent = 0.2 + numOfIterations = 100 + + population = generatePopulation(populationSize, genesLength, minGene, maxGene) + + num_gen, best = geneticAlgorithm(population, + elitePercent, + mutationPercent, + makeCrossoverFunc([1;3]), + numOfIterations) + + @test fitFunction(best) < 0.1 # Test if the absolute error is smaller than 0.1 +end \ No newline at end of file diff --git a/test/runtests.jl b/test/runtests.jl index 5d89c25..4de0047 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,6 +1,5 @@ using Test @testset "Tests" begin - a = 5 - @test a == 5 + include("genetic_algorithm.jl") end \ No newline at end of file From f9727dcb3ccf01bff0f50c410530d7880afb6563 Mon Sep 17 00:00:00 2001 From: Nikola-Mircic Date: Thu, 4 Jan 2024 23:12:28 +0100 Subject: [PATCH 2/7] Fix: test errors --- src/FTN_PSI_PA.jl | 2 ++ src/genetic_algorithm/genetic_algorithm.jl | 25 +++++++++++++--------- test/genetic_algorithm.jl | 6 +++--- test/runtests.jl | 1 + 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/FTN_PSI_PA.jl b/src/FTN_PSI_PA.jl index 65e4838..64c6faa 100644 --- a/src/FTN_PSI_PA.jl +++ b/src/FTN_PSI_PA.jl @@ -2,6 +2,8 @@ module FTN_PSI_PA import Printf +export GeneticAlgorithm + include("genetic_algorithm/genetic_algorithm.jl") include("pso/pso.jl") diff --git a/src/genetic_algorithm/genetic_algorithm.jl b/src/genetic_algorithm/genetic_algorithm.jl index 5fc0b84..0130622 100644 --- a/src/genetic_algorithm/genetic_algorithm.jl +++ b/src/genetic_algorithm/genetic_algorithm.jl @@ -2,6 +2,20 @@ module GeneticAlgorithm include("crossover.jl") +export makeCrossoverFunc +export generatePopulation + +export fitFunction + +export geneticAlgorithm + +export getAverage +export analyzeElitePercentage +export analyzeMutationPercentage +export analyzeNumberOfIteration +export analyzePopulationSize + +export printResult function geneticAlgorithm(population::Vector{Entity}, elitePercent::Float64, mutationPercent::Float64, crossoverFunc!::Function, iter::Int) bestFitnes = [] @@ -58,7 +72,7 @@ function getAverage(population::Vector{Entity}, elitePercent::Float64, mutationP elitePercent, mutationPercent, crossoverFunc!, - numOfIterations) + iter) avg_gen += gen_i avg_best += best_i.fitness end @@ -150,13 +164,4 @@ function printResult(values, results) end end -export geneticAlgorithm -export getAverage -export analyzeElitePercentage -export analyzeMutationPercentage -export analyzeNumberOfIteration -export analyzePopulationSize - -export printResult - end \ No newline at end of file diff --git a/test/genetic_algorithm.jl b/test/genetic_algorithm.jl index 09661bf..7ee3416 100644 --- a/test/genetic_algorithm.jl +++ b/test/genetic_algorithm.jl @@ -1,4 +1,4 @@ -using GeneticAlgorithm +using FTN_PSI_PA.GeneticAlgorithm @testset "GeneticAlgorithm" begin #= @@ -19,11 +19,11 @@ using GeneticAlgorithm population = generatePopulation(populationSize, genesLength, minGene, maxGene) - num_gen, best = geneticAlgorithm(population, + num_gen, best = getAverage(population, elitePercent, mutationPercent, makeCrossoverFunc([1;3]), numOfIterations) - @test fitFunction(best) < 0.1 # Test if the absolute error is smaller than 0.1 + @test best < 0.1 # Test if the absolute error is smaller than 0.1 end \ No newline at end of file diff --git a/test/runtests.jl b/test/runtests.jl index 4de0047..1bca4ae 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,3 +1,4 @@ +using FTN_PSI_PA using Test @testset "Tests" begin From 8988ed0a6daee217f429d2cd45ae18adc94b4393 Mon Sep 17 00:00:00 2001 From: Nikola-Mircic Date: Sat, 20 Jan 2024 02:09:54 +0100 Subject: [PATCH 3/7] Elitist selection --- src/genetic_algorithm/genetic_algorithm.jl | 41 +++++++++------------- src/genetic_algorithm/selection.jl | 19 ++++++++++ test/genetic_algorithm.jl | 2 +- 3 files changed, 37 insertions(+), 25 deletions(-) create mode 100644 src/genetic_algorithm/selection.jl diff --git a/src/genetic_algorithm/genetic_algorithm.jl b/src/genetic_algorithm/genetic_algorithm.jl index 0130622..7c5725f 100644 --- a/src/genetic_algorithm/genetic_algorithm.jl +++ b/src/genetic_algorithm/genetic_algorithm.jl @@ -1,12 +1,15 @@ module GeneticAlgorithm -include("crossover.jl") +include("selection.jl") -export makeCrossoverFunc -export generatePopulation +export generatePopulation export fitFunction +export makeCrossoverFunc + +export elitistSelection + export geneticAlgorithm export getAverage @@ -17,7 +20,7 @@ export analyzePopulationSize export printResult -function geneticAlgorithm(population::Vector{Entity}, elitePercent::Float64, mutationPercent::Float64, crossoverFunc!::Function, iter::Int) +function geneticAlgorithm(population::Vector{Entity}, selectAndCross::Function, mutationPercent::Float64, crossoverFunc!::Function, iter::Int) bestFitnes = [] updatePopulationFitness!(population, fitFunction) @@ -25,20 +28,10 @@ function geneticAlgorithm(population::Vector{Entity}, elitePercent::Float64, mut push!(bestFitnes, population[1].fitness) while !shouldStop(iter, bestFitnes) - n = length(population) - - eliteNumber = Int(trunc(elitePercent*n)); - - eliteNumber = eliteNumber + (n-eliteNumber)%2 - - elite = deepcopy(population[1:eliteNumber]) - - population = crossover!(population[eliteNumber+1:end], crossoverFunc!) + population = selectAndCross(population, crossoverFunc!) mutatePopulation!(population, mutationPercent) - population = [population; elite] - updatePopulationFitness!(population, fitFunction) push!(bestFitnes, population[1].fitness) @@ -63,16 +56,16 @@ end # ANALYZATION -function getAverage(population::Vector{Entity}, elitePercent::Float64, mutationPercent::Float64, crossoverFunc!::Function, iter::Int) +function getAverage(population::Vector{Entity}, selectAndCross::Function, mutationPercent::Float64, crossoverFunc!::Function, iter::Int) avg_gen = 0 avg_best = 0 for i in 1:100 gen_i, best_i = geneticAlgorithm(population, - elitePercent, - mutationPercent, - crossoverFunc!, - iter) + selectAndCross, + mutationPercent, + crossoverFunc!, + iter) avg_gen += gen_i avg_best += best_i.fitness end @@ -80,7 +73,7 @@ function getAverage(population::Vector{Entity}, elitePercent::Float64, mutationP return (avg_gen/100, avg_best/100) end -function analyzeElitePercentage(values::Array{Float64}) +function analyzeElitePercentage(values::Array{Function}) results = zeros(length(values), 2) for i in 1:length(values) @@ -106,7 +99,7 @@ function analyzeMutationPercentage(values::Array{Float64}) population_i = generatePopulation(populationSize, genesLength, minGene, maxGene) gen_i, best_i = getAverage(population_i, - elitePercent, + elitistSelection(elitePercent), values[i], makeCrossoverFunc([1;3]), numOfIterations) @@ -125,7 +118,7 @@ function analyzePopulationSize(values::Vector{Int}) population_i = generatePopulation(values[i], genesLength, minGene, maxGene) gen_i, best_i = getAverage(population_i, - elitePercent, + elitistSelection(elitePercent), mutationPercent, makeCrossoverFunc([1;3]), numOfIterations) @@ -144,7 +137,7 @@ function analyzeNumberOfIteration(values::Vector{Int}) population_i = generatePopulation(populationSize, genesLength, minGene, maxGene) gen_i, best_i = getAverage(population_i, - elitePercent, + elitistSelection(elitePercent), mutationPercent, makeCrossoverFunc([1;3]), values[i]) diff --git a/src/genetic_algorithm/selection.jl b/src/genetic_algorithm/selection.jl new file mode 100644 index 0000000..07bd671 --- /dev/null +++ b/src/genetic_algorithm/selection.jl @@ -0,0 +1,19 @@ +include("crossover.jl") + +function elitistSelection(elitePercent::Float64) + function selectionMethod(population::Vector{Entity}, crossFunc!::Function) + n = length(population) + + eliteNumber = Int(trunc(elitePercent*n)); + + eliteNumber = eliteNumber + (n-eliteNumber)%2 + + elite = deepcopy(population[1:eliteNumber]) + + population = crossover!(population[eliteNumber+1:end], crossFunc!) + + population = [population; elite] + + return population + end +end \ No newline at end of file diff --git a/test/genetic_algorithm.jl b/test/genetic_algorithm.jl index 7ee3416..d3f424d 100644 --- a/test/genetic_algorithm.jl +++ b/test/genetic_algorithm.jl @@ -20,7 +20,7 @@ using FTN_PSI_PA.GeneticAlgorithm population = generatePopulation(populationSize, genesLength, minGene, maxGene) num_gen, best = getAverage(population, - elitePercent, + elitistSelection(elitePercent), mutationPercent, makeCrossoverFunc([1;3]), numOfIterations) From ad03c692e746dfb15ebefb9af0a79bf8a10c9593 Mon Sep 17 00:00:00 2001 From: Nikola-Mircic Date: Wed, 7 Feb 2024 21:59:53 +0100 Subject: [PATCH 4/7] Roulette Wheel Selection --- src/genetic_algorithm/genetic_algorithm.jl | 1 + src/genetic_algorithm/selection.jl | 46 ++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/src/genetic_algorithm/genetic_algorithm.jl b/src/genetic_algorithm/genetic_algorithm.jl index 7c5725f..06a47b4 100644 --- a/src/genetic_algorithm/genetic_algorithm.jl +++ b/src/genetic_algorithm/genetic_algorithm.jl @@ -9,6 +9,7 @@ export fitFunction export makeCrossoverFunc export elitistSelection +export rouletteWheelSelection export geneticAlgorithm diff --git a/src/genetic_algorithm/selection.jl b/src/genetic_algorithm/selection.jl index 07bd671..55e45b0 100644 --- a/src/genetic_algorithm/selection.jl +++ b/src/genetic_algorithm/selection.jl @@ -14,6 +14,52 @@ function elitistSelection(elitePercent::Float64) population = [population; elite] + return population + end +end + +function findIdx(arr, x) + l::Int = 1; + r::Int = length(arr) + + + s = l + div(r-l,2) + idx = s + while l <= r + if x <= arr[s] + idx = s + r = s-1 + else + l = s+1 + end + + s = l + div(r-l,2) + end + + return idx +end + +function rouletteWheelSelection() + function selectionMethod(population::Vector{Entity}, crossFunc!::Function) + n = length(population) + + px = population .|> fitFunction + + totalFit = sum(px) + + px = px .|> (x)->x/totalFit + + for i=2:length(px) + px[i] = px[i] + px[i-1] + end + + for i=1:div(n,2) + idx1 = findIdx(px, rand()) + idx2 = findIdx(px, rand()) + + crossFunc!(population[idx1], population[idx2]) + end + return population end end \ No newline at end of file From e2daebb5afd51e57197e26d39d11e1129d351544 Mon Sep 17 00:00:00 2001 From: Nikola-Mircic Date: Wed, 7 Feb 2024 22:00:30 +0100 Subject: [PATCH 5/7] Roulette Wheel Selection : Test --- test/genetic_algorithm.jl | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/test/genetic_algorithm.jl b/test/genetic_algorithm.jl index d3f424d..a8f7ecc 100644 --- a/test/genetic_algorithm.jl +++ b/test/genetic_algorithm.jl @@ -18,12 +18,24 @@ using FTN_PSI_PA.GeneticAlgorithm numOfIterations = 100 population = generatePopulation(populationSize, genesLength, minGene, maxGene) + + @testset "Elitistic Selection: " begin + num_gen, best = getAverage(population, + elitistSelection(elitePercent), + mutationPercent, + makeCrossoverFunc([1;3]), + numOfIterations) - num_gen, best = getAverage(population, - elitistSelection(elitePercent), - mutationPercent, - makeCrossoverFunc([1;3]), - numOfIterations) + @test best < 0.1 # Test if the absolute error is smaller than 0.1 + end - @test best < 0.1 # Test if the absolute error is smaller than 0.1 + @testset "Roulette Wheel Selection: " begin + num_gen, best = getAverage(population, + rouletteWheelSelection(), + mutationPercent, + makeCrossoverFunc([1;3]), + numOfIterations) + + @test best < 0.1 # Test if the absolute error is smaller than 0.1 + end end \ No newline at end of file From 5b22f42396e5575c285df2d94279f2b279991cf9 Mon Sep 17 00:00:00 2001 From: Nikola-Mircic Date: Mon, 25 Mar 2024 23:09:06 +0100 Subject: [PATCH 6/7] Renamed project --- Project.toml | 2 +- src/{FTN_PSI_PA.jl => MetaheuristicAlgos.jl} | 2 +- test/genetic_algorithm.jl | 2 +- test/runtests.jl | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename src/{FTN_PSI_PA.jl => MetaheuristicAlgos.jl} (81%) diff --git a/Project.toml b/Project.toml index e4ad775..a35f758 100644 --- a/Project.toml +++ b/Project.toml @@ -1,4 +1,4 @@ -name = "FTN_PSI_PA" +name = "MetaheuristicAlgos" uuid = "0afddabe-dfaf-4480-bf01-29bfefedff8e" version = "0.1.2" authors = ["Nikola-Mircic"] diff --git a/src/FTN_PSI_PA.jl b/src/MetaheuristicAlgos.jl similarity index 81% rename from src/FTN_PSI_PA.jl rename to src/MetaheuristicAlgos.jl index 64c6faa..02fdef3 100644 --- a/src/FTN_PSI_PA.jl +++ b/src/MetaheuristicAlgos.jl @@ -1,4 +1,4 @@ -module FTN_PSI_PA +module MetaheuristicAlgos import Printf diff --git a/test/genetic_algorithm.jl b/test/genetic_algorithm.jl index a8f7ecc..8b0d383 100644 --- a/test/genetic_algorithm.jl +++ b/test/genetic_algorithm.jl @@ -1,4 +1,4 @@ -using FTN_PSI_PA.GeneticAlgorithm +using .GeneticAlgorithm @testset "GeneticAlgorithm" begin #= diff --git a/test/runtests.jl b/test/runtests.jl index 1bca4ae..3c55526 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,4 +1,4 @@ -using FTN_PSI_PA +using MetaheuristicAlgos using Test @testset "Tests" begin From 3014a689c61a402909e1e35f96dd3603ae7722a6 Mon Sep 17 00:00:00 2001 From: Nikola Mircic Date: Mon, 25 Mar 2024 23:22:03 +0100 Subject: [PATCH 7/7] Update README.md --- README.md | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2afa183..372b1de 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,17 @@ -# ftn_psi_pa -Sample codes for Applied algorithms course on Faculty of Technical Sciences, University of Novi Sad +# Metaheuristic algorithms + +### What are these algorithms? +Metaheuristic algorithms are optimization algorithms that are used to address complicated issues that cannot be solved using standard approaches. + +These algorithms are inspired by natural processes such as **genetics, swarm behavior, and evolution**, and they are used to explore a broad search space to identify the global optimum of a problem. + + +### Examples of some metaheuristic algorithms implemented in Julia: + +- [Genetic algorithm](/src/genetic_algorithm) +- [Particle swarm optimization](/src/pso) + +
+ +TO-DO: +- Ant colony optimization