Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Squirrel search algorithm implementation #338

Open
firefly-cpp opened this issue Jul 1, 2021 · 7 comments
Open

Squirrel search algorithm implementation #338

firefly-cpp opened this issue Jul 1, 2021 · 7 comments
Assignees
Labels
enhancement New feature or request help wanted Extra attention is needed

Comments

@firefly-cpp
Copy link
Contributor

Squirrel search algorithm publication is currently the most cited article in SWEVO.

It would be nice to have this algorithm in our collection.

Has anyone time to implement this algorithm?

@firefly-cpp firefly-cpp added enhancement New feature or request help wanted Extra attention is needed labels Jul 1, 2021
@altaregos
Copy link

can you assign me on this?

@firefly-cpp
Copy link
Contributor Author

@altaregos, thanks. Yeah. Of course.

@zStupan
Copy link
Contributor

zStupan commented Mar 5, 2022

I've made an attempt:

import numpy as np
from niapy.algorithms import Algorithm
from niapy.util.random import levy_flight
from niapy.task import Task


class SquirrelSearchAlgorithm(Algorithm):
    Name = ['SquirrelSearchAlgorithm', 'SSA']
    
    def __init__(self, population_size=50, food_sources=4, prob_predation=0.1, gliding_constant=1.9, scale=18, *args, **kwargs):
        super().__init__(population_size, *args, **kwargs)
        self.food_sources = food_sources
        self.prob_predation = prob_predation
        self.gliding_constant = gliding_constant
        self.scale = scale

    def set_parameters(self, population_size=50, food_sources=4, prob_predation=0.1, gliding_constant=1.9, scale=18, *args, **kwargs):
        super().set_parameters(population_size, *args, **kwargs)
        self.food_sources = food_sources
        self.prob_predation = prob_predation
        self.gliding_constant = gliding_constant
        self.scale = scale

    def get_parameters(self):
        params = super().get_parameters()
        params.update({
            'food_sources': self.food_sources,
            'prob_predation': self.prob_predation,
            'gliding_constant': self.gliding_constant,
            'scale': self.scale,
        })
        return params
    
    def gliding_distance(self):
        lift = 0.9783723933835806 / self.uniform(0.675, 1.5)
        drag = 1.630620655639301
        return 8.0 / (self.scale * drag / lift)
    
    def run_iteration(self, task, population, population_fitness, best_x, best_fitness, **params):
        indices = np.argsort(population_fitness)
        ht = indices[0]
        at = indices[1:self.food_sources]
        nt = indices[self.food_sources:]

        new_population = population.copy()

        for index in at:
            if self.random() >= self.prob_predation:
                new_population[index] += self.gliding_distance() * self.gliding_constant * (population[ht] - population[index])
                new_population[index] = task.repair(new_population[index], rng=self.rng)
            else:
                new_population[index] = self.uniform(task.lower, task.upper)
        
        nt = self.rng.permutation(nt)
        nt_1 = nt[:len(nt) // 2] # half go to acorn trees
        nt_2 = nt[len(nt) // 2:] # other half go to hickory trees
        
        for index in nt_1:
            if self.random() >= self.prob_predation:
                a = self.rng.choice(at)
                new_population[index] += self.gliding_distance() * self.gliding_constant * (population[a] - population[index])
                new_population[index] = task.repair(new_population[index], rng=self.rng)
            else:
                new_population[index] = self.uniform(task.lower, task.upper)

        for index in nt_2:
            if self.random() >= self.prob_predation:
                new_population[index] += self.gliding_distance() * self.gliding_constant * (population[ht] - population[index])
                new_population[index] = task.repair(new_population[index], rng=self.rng)
            else:
                new_population[index] = self.uniform(task.lower, task.upper)

        s_min = 1e-5 / (365 ** ((task.iters + 1) / (task.max_iters / 2.5)))
        sc = np.sqrt(np.sum((new_population[at] - new_population[ht]) ** 2))

        if sc < s_min:
            new_population[nt_1] = task.lower + levy_flight(size=(len(nt_1), task.dimension), rng=self.rng) * task.range
            new_population[nt_1] = task.repair(new_population[nt_1], rng=self.rng)

        new_fitness = np.apply_along_axis(task.eval, 1, new_population)
        best_x, best_fitness = self.get_best(new_population, new_fitness, best_x, best_fitness)
        
        return new_population, new_fitness, best_x, best_fitness, {}


if __name__ == '__main__':
    fit = []
    for i in range(30):
        task = Task('sphere', dimension=30, lower=-10, upper=10, max_iters=500)
        algo = SquirrelSearchAlgorithm()
        _, best_fitness = algo.run(task)
        fit.append(best_fitness)
    
    print(f'Best: {np.min(fit)}')
    print(f'Worst: {np.max(fit)}')
    print(f'Mean: {np.mean(fit)}')
    print(f'Std: {np.std(fit)}')

But I don't get the same results as the ones in the article. For this example (30 runs, 30-D sphere on [-10, 10], max_iters=500) i get:

Best: 7.5371836448343156e-06
Worst: 8.828262744205666e-05
Mean: 4.013667587147927e-05
Std: 2.289286326787123e-05

and in the article they got:

Best: 7.9225e-20
Worst: 5.7411e-07
Mean: 4.1689E-08
Std: 1.4356E-07

The article is extremely unclear, in my opinion. At least it was to me. It doesn't say how to choose which squirrels on normal trees go towards acorn trees and which towards the hickory tree, it just says "do it randomly". I just chose half randomly. Also, I don't know if I'm calculating the season constant correctly, because the equation is formatted weirdly and there's no real explanation of it. And then if the seasons change, I don't know which squirrels to move via levy flights.

Any ideas what I'm doing wrong? Otherwise I'd say it's best just not to include it.

@BrandonDuncan13
Copy link

@zStupan, I'm confused by what you mean. Aren't the SSA results going to be different each time you run it? I haven't looked into this much, but just putting ideas out there

@zStupan
Copy link
Contributor

zStupan commented Apr 5, 2024

@BrandonDuncan13 yes the results are different each time, but I would expect that the average fitness over 30 runs would be of similar magnitude as the results in the paper.

@BrandonDuncan13
Copy link

@zStupan Ohh, okay, I see what you're talking about now. Yeah I agree the average fitness should be a similar magnitude. I will spend time trying to improve your code within the next few weeks but I don't have much confidence in improving it haha

@BrandonDuncan13
Copy link

@Nehazzi If you're looking to apply this optimization algorithm to a coding project, I would either look at the MatLab code that exists for SSA or look at how other optimization algorithms have been applied to coding projects. Then you can figure out how to apply the SSA code above to your coding project. Idk what you're asking but I think that's what you were looking for

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

4 participants