# -*- coding: utf-8 -*-

"""PSO module

Copyright (c) 2017 Future Processing sp. z.o.o.

@author: Pablo Ribalta Lorenzo
@email: pribalta@future-processing.com)
@date: 10.04.2017

This module encapsulates all the functionality related to Particle
Swarm Optimization, including the algorithm itself and the Particles
"""

import copy
import numpy as np

class Particle(object):
    """Particle class for PSO

    This class encapsulates the behavior of each particle in PSO and provides
    an efficient way to do bookkeeping about the state of the swarm in any given
    iteration.

    Args:
        lower_bound (np.array): Vector of lower boundaries for particle dimensions.
        upper_bound (np.array): Vector of upper boundaries for particle dimensions.
        dimensions (int): Number of dimensions of the search space.
        objective function (function): Black-box function to evaluate.

    """
    def __init__(self,
                 lower_bound,
                 upper_bound,
                 dimensions,
                 objective_function):
        self.reset(dimensions, lower_bound, upper_bound, objective_function)

    def reset(self,
              dimensions,
              lower_bound,
              upper_bound,
              objective_function):
        """Particle reset

        Allows for reset of a particle without reallocation.

		Args:
			lower_bound (np.array): Vector of lower boundaries for particle dimensions.
			upper_bound (np.array): Vector of upper boundaries for particle dimensions.
			dimensions (int): Number of dimensions of the search space.

        """
        position = []
        for i in range(dimensions):
            if lower_bound[i] < upper_bound[i]:
                position.extend(np.random.randint(lower_bound[i], upper_bound[i] + 1, 1, dtype=int))
            elif lower_bound[i] == upper_bound[i]:
                position.extend(np.array([lower_bound[i]], dtype=int))
            else:
                assert False

        self.position = [position]

        self.velocity = [np.multiply(np.random.rand(dimensions),
                                     (upper_bound - lower_bound)).astype(int)]

        self.best_position = self.position[:]

        self.function_value = [objective_function(self.best_position[-1])]
        self.best_function_value = self.function_value[:]

    def update_velocity(self, omega, phip, phig, best_swarm_position):
        """Particle velocity update

		Args:
			omega (float): Velocity equation constant.
			phip (float): Velocity equation constant.
			phig (float): Velocity equation constant.
			best_swarm_position (np.array): Best particle position.

        """
        random_coefficient_p = np.random.uniform(size=np.asarray(self.position[-1]).shape)
        random_coefficient_g = np.random.uniform(size=np.asarray(self.position[-1]).shape)

        self.velocity.append(omega
                             * np.asarray(self.velocity[-1])
                             + phip
                             * random_coefficient_p
                             * (np.asarray(self.best_position[-1])
                                - np.asarray(self.position[-1]))
                             + phig
                             * random_coefficient_g
                             * (np.asarray(best_swarm_position)
                                - np.asarray(self.position[-1])))

        self.velocity[-1] = self.velocity[-1].astype(int)

    def update_position(self, lower_bound, upper_bound, objective_function):
        """Particle position update

		Args:
			lower_bound (np.array): Vector of lower boundaries for particle dimensions.
			upper_bound (np.array): Vector of upper boundaries for particle dimensions.
			objective function (function): Black-box function to evaluate.

        """
        new_position = self.position[-1] + self.velocity[-1]

        if np.array_equal(self.position[-1], new_position):
            self.function_value.append(self.function_value[-1])
        else:
            mark1 = new_position < lower_bound
            mark2 = new_position > upper_bound

            new_position[mark1] = lower_bound[mark1]
            new_position[mark2] = upper_bound[mark2]

            self.function_value.append(objective_function(self.position[-1]))

        self.position.append(new_position.tolist())

        if self.function_value[-1] < self.best_function_value[-1]:
            self.best_position.append(self.position[-1][:])
            self.best_function_value.append(self.function_value[-1])

class Pso(object):
    """PSO wrapper

    This class contains the particles and provides an abstraction to hold all the context
    of the PSO algorithm

    Args:
        swarmsize (int): Number of particles in the swarm
        maxiter (int): Maximum number of generations the swarm will run

    """
    def __init__(self, swarmsize=100, maxiter=100):
        self.max_generations = maxiter
        self.swarmsize = swarmsize

        self.omega = 0.5
        self.phip = 0.5
        self.phig = 0.5

        self.minstep = 1e-4
        self.minfunc = 1e-4

        self.best_position = [None]
        self.best_function_value = [1]

        self.particles = []

        self.retired_particles = []

    def run(self, function, lower_bound, upper_bound, kwargs=None):
        """Perform a particle swarm optimization (PSO)

		Args:
			objective_function (function): The function to be minimized.
			lower_bound (np.array): Vector of lower boundaries for particle dimensions.
			upper_bound (np.array): Vector of upper boundaries for particle dimensions.

		Returns:
			best_position (np.array): Best known position
			accuracy (float): Objective value at best_position
			:param kwargs:

        """
        if kwargs is None:
            kwargs = {}

        objective_function = lambda x: function(x, **kwargs)
        assert hasattr(function, '__call__'), 'Invalid function handle'

        assert len(lower_bound) == len(upper_bound), 'Invalid bounds length'

        lower_bound = np.array(lower_bound)
        upper_bound = np.array(upper_bound)

        assert np.all(upper_bound > lower_bound), 'Invalid boundary values'


        dimensions = len(lower_bound)

        self.particles = self.initialize_particles(lower_bound,
                                                   upper_bound,
                                                   dimensions,
                                                   objective_function)

        # Start evolution
        generation = 1
        while generation <= self.max_generations:
            for particle in self.particles:
                particle.update_velocity(self.omega, self.phip, self.phig, self.best_position[-1])
                particle.update_position(lower_bound, upper_bound, objective_function)

                if particle.best_function_value[-1] == 0:
                    self.retired_particles.append(copy.deepcopy(particle))
                    particle.reset(dimensions, lower_bound, upper_bound, objective_function)
                elif particle.best_function_value[-1] < self.best_function_value[-1]:
                    stepsize = np.sqrt(np.sum((np.asarray(self.best_position[-1])
                                               - np.asarray(particle.position[-1])) ** 2))

                    if np.abs(np.asarray(self.best_function_value[-1])
                              - np.asarray(particle.best_function_value[-1])) \
                            <= self.minfunc:
                        return particle.best_position[-1], particle.best_function_value[-1]
                    elif stepsize <= self.minstep:
                        return particle.best_position[-1], particle.best_function_value[-1]
                    else:
                        self.best_function_value.append(particle.best_function_value[-1])
                        self.best_position.append(particle.best_position[-1][:])



            generation += 1

        return self.best_position[-1], self.best_function_value[-1]

    def initialize_particles(self,
                             lower_bound,
                             upper_bound,
                             dimensions,
                             objective_function):
        """Initializes the particles for the swarm

		Args:
			objective_function (function): The function to be minimized.
			lower_bound (np.array): Vector of lower boundaries for particle dimensions.
			upper_bound (np.array): Vector of upper boundaries for particle dimensions.
			dimensions (int): Number of dimensions of the search space.

		Returns:
			particles (list): Collection or particles in the swarm

        """
        particles = []
        for _ in range(self.swarmsize):
            particles.append(Particle(lower_bound,
                                      upper_bound,
                                      dimensions,
                                      objective_function))
            if particles[-1].best_function_value[-1] < self.best_function_value[-1]:
                self.best_function_value.append(particles[-1].best_function_value[-1])
                self.best_position.append(particles[-1].best_position[-1])


        self.best_position = [self.best_position[-1]]
        self.best_function_value = [self.best_function_value[-1]]

        return particles
9e5ce94a95472068b983a1d59540a6aa148084e1
<mask token>


class Particle(object):
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>


class Pso(object):
    """PSO wrapper

    This class contains the particles and provides an abstraction to hold all the context
    of the PSO algorithm

    Args:
        swarmsize (int): Number of particles in the swarm
        maxiter (int): Maximum number of generations the swarm will run

    """

    def __init__(self, swarmsize=100, maxiter=100):
        self.max_generations = maxiter
        self.swarmsize = swarmsize
        self.omega = 0.5
        self.phip = 0.5
        self.phig = 0.5
        self.minstep = 0.0001
        self.minfunc = 0.0001
        self.best_position = [None]
        self.best_function_value = [1]
        self.particles = []
        self.retired_particles = []

    def run(self, function, lower_bound, upper_bound, kwargs=None):
        """Perform a particle swarm optimization (PSO)

		Args:
			objective_function (function): The function to be minimized.
			lower_bound (np.array): Vector of lower boundaries for particle dimensions.
			upper_bound (np.array): Vector of upper boundaries for particle dimensions.

		Returns:
			best_position (np.array): Best known position
			accuracy (float): Objective value at best_position
			:param kwargs:

        """
        if kwargs is None:
            kwargs = {}
        objective_function = lambda x: function(x, **kwargs)
        assert hasattr(function, '__call__'), 'Invalid function handle'
        assert len(lower_bound) == len(upper_bound), 'Invalid bounds length'
        lower_bound = np.array(lower_bound)
        upper_bound = np.array(upper_bound)
        assert np.all(upper_bound > lower_bound), 'Invalid boundary values'
        dimensions = len(lower_bound)
        self.particles = self.initialize_particles(lower_bound, upper_bound,
            dimensions, objective_function)
        generation = 1
        while generation <= self.max_generations:
            for particle in self.particles:
                particle.update_velocity(self.omega, self.phip, self.phig,
                    self.best_position[-1])
                particle.update_position(lower_bound, upper_bound,
                    objective_function)
                if particle.best_function_value[-1] == 0:
                    self.retired_particles.append(copy.deepcopy(particle))
                    particle.reset(dimensions, lower_bound, upper_bound,
                        objective_function)
                elif particle.best_function_value[-1
                    ] < self.best_function_value[-1]:
                    stepsize = np.sqrt(np.sum((np.asarray(self.
                        best_position[-1]) - np.asarray(particle.position[-
                        1])) ** 2))
                    if np.abs(np.asarray(self.best_function_value[-1]) - np
                        .asarray(particle.best_function_value[-1])
                        ) <= self.minfunc:
                        return particle.best_position[-1
                            ], particle.best_function_value[-1]
                    elif stepsize <= self.minstep:
                        return particle.best_position[-1
                            ], particle.best_function_value[-1]
                    else:
                        self.best_function_value.append(particle.
                            best_function_value[-1])
                        self.best_position.append(particle.best_position[-1][:]
                            )
            generation += 1
        return self.best_position[-1], self.best_function_value[-1]

    def initialize_particles(self, lower_bound, upper_bound, dimensions,
        objective_function):
        """Initializes the particles for the swarm

		Args:
			objective_function (function): The function to be minimized.
			lower_bound (np.array): Vector of lower boundaries for particle dimensions.
			upper_bound (np.array): Vector of upper boundaries for particle dimensions.
			dimensions (int): Number of dimensions of the search space.

		Returns:
			particles (list): Collection or particles in the swarm

        """
        particles = []
        for _ in range(self.swarmsize):
            particles.append(Particle(lower_bound, upper_bound, dimensions,
                objective_function))
            if particles[-1].best_function_value[-1
                ] < self.best_function_value[-1]:
                self.best_function_value.append(particles[-1].
                    best_function_value[-1])
                self.best_position.append(particles[-1].best_position[-1])
        self.best_position = [self.best_position[-1]]
        self.best_function_value = [self.best_function_value[-1]]
        return particles

<mask token>


class Particle(object):
    <mask token>

    def __init__(self, lower_bound, upper_bound, dimensions, objective_function
        ):
        self.reset(dimensions, lower_bound, upper_bound, objective_function)
    <mask token>
    <mask token>

    def update_position(self, lower_bound, upper_bound, objective_function):
        """Particle position update

		Args:
			lower_bound (np.array): Vector of lower boundaries for particle dimensions.
			upper_bound (np.array): Vector of upper boundaries for particle dimensions.
			objective function (function): Black-box function to evaluate.

        """
        new_position = self.position[-1] + self.velocity[-1]
        if np.array_equal(self.position[-1], new_position):
            self.function_value.append(self.function_value[-1])
        else:
            mark1 = new_position < lower_bound
            mark2 = new_position > upper_bound
            new_position[mark1] = lower_bound[mark1]
            new_position[mark2] = upper_bound[mark2]
            self.function_value.append(objective_function(self.position[-1]))
        self.position.append(new_position.tolist())
        if self.function_value[-1] < self.best_function_value[-1]:
            self.best_position.append(self.position[-1][:])
            self.best_function_value.append(self.function_value[-1])


class Pso(object):
    """PSO wrapper

    This class contains the particles and provides an abstraction to hold all the context
    of the PSO algorithm

    Args:
        swarmsize (int): Number of particles in the swarm
        maxiter (int): Maximum number of generations the swarm will run

    """

    def __init__(self, swarmsize=100, maxiter=100):
        self.max_generations = maxiter
        self.swarmsize = swarmsize
        self.omega = 0.5
        self.phip = 0.5
        self.phig = 0.5
        self.minstep = 0.0001
        self.minfunc = 0.0001
        self.best_position = [None]
        self.best_function_value = [1]
        self.particles = []
        self.retired_particles = []

    def run(self, function, lower_bound, upper_bound, kwargs=None):
        """Perform a particle swarm optimization (PSO)

		Args:
			objective_function (function): The function to be minimized.
			lower_bound (np.array): Vector of lower boundaries for particle dimensions.
			upper_bound (np.array): Vector of upper boundaries for particle dimensions.

		Returns:
			best_position (np.array): Best known position
			accuracy (float): Objective value at best_position
			:param kwargs:

        """
        if kwargs is None:
            kwargs = {}
        objective_function = lambda x: function(x, **kwargs)
        assert hasattr(function, '__call__'), 'Invalid function handle'
        assert len(lower_bound) == len(upper_bound), 'Invalid bounds length'
        lower_bound = np.array(lower_bound)
        upper_bound = np.array(upper_bound)
        assert np.all(upper_bound > lower_bound), 'Invalid boundary values'
        dimensions = len(lower_bound)
        self.particles = self.initialize_particles(lower_bound, upper_bound,
            dimensions, objective_function)
        generation = 1
        while generation <= self.max_generations:
            for particle in self.particles:
                particle.update_velocity(self.omega, self.phip, self.phig,
                    self.best_position[-1])
                particle.update_position(lower_bound, upper_bound,
                    objective_function)
                if particle.best_function_value[-1] == 0:
                    self.retired_particles.append(copy.deepcopy(particle))
                    particle.reset(dimensions, lower_bound, upper_bound,
                        objective_function)
                elif particle.best_function_value[-1
                    ] < self.best_function_value[-1]:
                    stepsize = np.sqrt(np.sum((np.asarray(self.
                        best_position[-1]) - np.asarray(particle.position[-
                        1])) ** 2))
                    if np.abs(np.asarray(self.best_function_value[-1]) - np
                        .asarray(particle.best_function_value[-1])
                        ) <= self.minfunc:
                        return particle.best_position[-1
                            ], particle.best_function_value[-1]
                    elif stepsize <= self.minstep:
                        return particle.best_position[-1
                            ], particle.best_function_value[-1]
                    else:
                        self.best_function_value.append(particle.
                            best_function_value[-1])
                        self.best_position.append(particle.best_position[-1][:]
                            )
            generation += 1
        return self.best_position[-1], self.best_function_value[-1]

    def initialize_particles(self, lower_bound, upper_bound, dimensions,
        objective_function):
        """Initializes the particles for the swarm

		Args:
			objective_function (function): The function to be minimized.
			lower_bound (np.array): Vector of lower boundaries for particle dimensions.
			upper_bound (np.array): Vector of upper boundaries for particle dimensions.
			dimensions (int): Number of dimensions of the search space.

		Returns:
			particles (list): Collection or particles in the swarm

        """
        particles = []
        for _ in range(self.swarmsize):
            particles.append(Particle(lower_bound, upper_bound, dimensions,
                objective_function))
            if particles[-1].best_function_value[-1
                ] < self.best_function_value[-1]:
                self.best_function_value.append(particles[-1].
                    best_function_value[-1])
                self.best_position.append(particles[-1].best_position[-1])
        self.best_position = [self.best_position[-1]]
        self.best_function_value = [self.best_function_value[-1]]
        return particles

<mask token>


class Particle(object):
    <mask token>

    def __init__(self, lower_bound, upper_bound, dimensions, objective_function
        ):
        self.reset(dimensions, lower_bound, upper_bound, objective_function)
    <mask token>

    def update_velocity(self, omega, phip, phig, best_swarm_position):
        """Particle velocity update

		Args:
			omega (float): Velocity equation constant.
			phip (float): Velocity equation constant.
			phig (float): Velocity equation constant.
			best_swarm_position (np.array): Best particle position.

        """
        random_coefficient_p = np.random.uniform(size=np.asarray(self.
            position[-1]).shape)
        random_coefficient_g = np.random.uniform(size=np.asarray(self.
            position[-1]).shape)
        self.velocity.append(omega * np.asarray(self.velocity[-1]) + phip *
            random_coefficient_p * (np.asarray(self.best_position[-1]) - np
            .asarray(self.position[-1])) + phig * random_coefficient_g * (
            np.asarray(best_swarm_position) - np.asarray(self.position[-1])))
        self.velocity[-1] = self.velocity[-1].astype(int)

    def update_position(self, lower_bound, upper_bound, objective_function):
        """Particle position update

		Args:
			lower_bound (np.array): Vector of lower boundaries for particle dimensions.
			upper_bound (np.array): Vector of upper boundaries for particle dimensions.
			objective function (function): Black-box function to evaluate.

        """
        new_position = self.position[-1] + self.velocity[-1]
        if np.array_equal(self.position[-1], new_position):
            self.function_value.append(self.function_value[-1])
        else:
            mark1 = new_position < lower_bound
            mark2 = new_position > upper_bound
            new_position[mark1] = lower_bound[mark1]
            new_position[mark2] = upper_bound[mark2]
            self.function_value.append(objective_function(self.position[-1]))
        self.position.append(new_position.tolist())
        if self.function_value[-1] < self.best_function_value[-1]:
            self.best_position.append(self.position[-1][:])
            self.best_function_value.append(self.function_value[-1])


class Pso(object):
    """PSO wrapper

    This class contains the particles and provides an abstraction to hold all the context
    of the PSO algorithm

    Args:
        swarmsize (int): Number of particles in the swarm
        maxiter (int): Maximum number of generations the swarm will run

    """

    def __init__(self, swarmsize=100, maxiter=100):
        self.max_generations = maxiter
        self.swarmsize = swarmsize
        self.omega = 0.5
        self.phip = 0.5
        self.phig = 0.5
        self.minstep = 0.0001
        self.minfunc = 0.0001
        self.best_position = [None]
        self.best_function_value = [1]
        self.particles = []
        self.retired_particles = []

    def run(self, function, lower_bound, upper_bound, kwargs=None):
        """Perform a particle swarm optimization (PSO)

		Args:
			objective_function (function): The function to be minimized.
			lower_bound (np.array): Vector of lower boundaries for particle dimensions.
			upper_bound (np.array): Vector of upper boundaries for particle dimensions.

		Returns:
			best_position (np.array): Best known position
			accuracy (float): Objective value at best_position
			:param kwargs:

        """
        if kwargs is None:
            kwargs = {}
        objective_function = lambda x: function(x, **kwargs)
        assert hasattr(function, '__call__'), 'Invalid function handle'
        assert len(lower_bound) == len(upper_bound), 'Invalid bounds length'
        lower_bound = np.array(lower_bound)
        upper_bound = np.array(upper_bound)
        assert np.all(upper_bound > lower_bound), 'Invalid boundary values'
        dimensions = len(lower_bound)
        self.particles = self.initialize_particles(lower_bound, upper_bound,
            dimensions, objective_function)
        generation = 1
        while generation <= self.max_generations:
            for particle in self.particles:
                particle.update_velocity(self.omega, self.phip, self.phig,
                    self.best_position[-1])
                particle.update_position(lower_bound, upper_bound,
                    objective_function)
                if particle.best_function_value[-1] == 0:
                    self.retired_particles.append(copy.deepcopy(particle))
                    particle.reset(dimensions, lower_bound, upper_bound,
                        objective_function)
                elif particle.best_function_value[-1
                    ] < self.best_function_value[-1]:
                    stepsize = np.sqrt(np.sum((np.asarray(self.
                        best_position[-1]) - np.asarray(particle.position[-
                        1])) ** 2))
                    if np.abs(np.asarray(self.best_function_value[-1]) - np
                        .asarray(particle.best_function_value[-1])
                        ) <= self.minfunc:
                        return particle.best_position[-1
                            ], particle.best_function_value[-1]
                    elif stepsize <= self.minstep:
                        return particle.best_position[-1
                            ], particle.best_function_value[-1]
                    else:
                        self.best_function_value.append(particle.
                            best_function_value[-1])
                        self.best_position.append(particle.best_position[-1][:]
                            )
            generation += 1
        return self.best_position[-1], self.best_function_value[-1]

    def initialize_particles(self, lower_bound, upper_bound, dimensions,
        objective_function):
        """Initializes the particles for the swarm

		Args:
			objective_function (function): The function to be minimized.
			lower_bound (np.array): Vector of lower boundaries for particle dimensions.
			upper_bound (np.array): Vector of upper boundaries for particle dimensions.
			dimensions (int): Number of dimensions of the search space.

		Returns:
			particles (list): Collection or particles in the swarm

        """
        particles = []
        for _ in range(self.swarmsize):
            particles.append(Particle(lower_bound, upper_bound, dimensions,
                objective_function))
            if particles[-1].best_function_value[-1
                ] < self.best_function_value[-1]:
                self.best_function_value.append(particles[-1].
                    best_function_value[-1])
                self.best_position.append(particles[-1].best_position[-1])
        self.best_position = [self.best_position[-1]]
        self.best_function_value = [self.best_function_value[-1]]
        return particles

<mask token>
import copy
import numpy as np


class Particle(object):
    """Particle class for PSO

    This class encapsulates the behavior of each particle in PSO and provides
    an efficient way to do bookkeeping about the state of the swarm in any given
    iteration.

    Args:
        lower_bound (np.array): Vector of lower boundaries for particle dimensions.
        upper_bound (np.array): Vector of upper boundaries for particle dimensions.
        dimensions (int): Number of dimensions of the search space.
        objective function (function): Black-box function to evaluate.

    """

    def __init__(self, lower_bound, upper_bound, dimensions, objective_function
        ):
        self.reset(dimensions, lower_bound, upper_bound, objective_function)

    def reset(self, dimensions, lower_bound, upper_bound, objective_function):
        """Particle reset

        Allows for reset of a particle without reallocation.

		Args:
			lower_bound (np.array): Vector of lower boundaries for particle dimensions.
			upper_bound (np.array): Vector of upper boundaries for particle dimensions.
			dimensions (int): Number of dimensions of the search space.

        """
        position = []
        for i in range(dimensions):
            if lower_bound[i] < upper_bound[i]:
                position.extend(np.random.randint(lower_bound[i], 
                    upper_bound[i] + 1, 1, dtype=int))
            elif lower_bound[i] == upper_bound[i]:
                position.extend(np.array([lower_bound[i]], dtype=int))
            else:
                assert False
        self.position = [position]
        self.velocity = [np.multiply(np.random.rand(dimensions), 
            upper_bound - lower_bound).astype(int)]
        self.best_position = self.position[:]
        self.function_value = [objective_function(self.best_position[-1])]
        self.best_function_value = self.function_value[:]

    def update_velocity(self, omega, phip, phig, best_swarm_position):
        """Particle velocity update

		Args:
			omega (float): Velocity equation constant.
			phip (float): Velocity equation constant.
			phig (float): Velocity equation constant.
			best_swarm_position (np.array): Best particle position.

        """
        random_coefficient_p = np.random.uniform(size=np.asarray(self.
            position[-1]).shape)
        random_coefficient_g = np.random.uniform(size=np.asarray(self.
            position[-1]).shape)
        self.velocity.append(omega * np.asarray(self.velocity[-1]) + phip *
            random_coefficient_p * (np.asarray(self.best_position[-1]) - np
            .asarray(self.position[-1])) + phig * random_coefficient_g * (
            np.asarray(best_swarm_position) - np.asarray(self.position[-1])))
        self.velocity[-1] = self.velocity[-1].astype(int)

    def update_position(self, lower_bound, upper_bound, objective_function):
        """Particle position update

		Args:
			lower_bound (np.array): Vector of lower boundaries for particle dimensions.
			upper_bound (np.array): Vector of upper boundaries for particle dimensions.
			objective function (function): Black-box function to evaluate.

        """
        new_position = self.position[-1] + self.velocity[-1]
        if np.array_equal(self.position[-1], new_position):
            self.function_value.append(self.function_value[-1])
        else:
            mark1 = new_position < lower_bound
            mark2 = new_position > upper_bound
            new_position[mark1] = lower_bound[mark1]
            new_position[mark2] = upper_bound[mark2]
            self.function_value.append(objective_function(self.position[-1]))
        self.position.append(new_position.tolist())
        if self.function_value[-1] < self.best_function_value[-1]:
            self.best_position.append(self.position[-1][:])
            self.best_function_value.append(self.function_value[-1])


class Pso(object):
    """PSO wrapper

    This class contains the particles and provides an abstraction to hold all the context
    of the PSO algorithm

    Args:
        swarmsize (int): Number of particles in the swarm
        maxiter (int): Maximum number of generations the swarm will run

    """

    def __init__(self, swarmsize=100, maxiter=100):
        self.max_generations = maxiter
        self.swarmsize = swarmsize
        self.omega = 0.5
        self.phip = 0.5
        self.phig = 0.5
        self.minstep = 0.0001
        self.minfunc = 0.0001
        self.best_position = [None]
        self.best_function_value = [1]
        self.particles = []
        self.retired_particles = []

    def run(self, function, lower_bound, upper_bound, kwargs=None):
        """Perform a particle swarm optimization (PSO)

		Args:
			objective_function (function): The function to be minimized.
			lower_bound (np.array): Vector of lower boundaries for particle dimensions.
			upper_bound (np.array): Vector of upper boundaries for particle dimensions.

		Returns:
			best_position (np.array): Best known position
			accuracy (float): Objective value at best_position
			:param kwargs:

        """
        if kwargs is None:
            kwargs = {}
        objective_function = lambda x: function(x, **kwargs)
        assert hasattr(function, '__call__'), 'Invalid function handle'
        assert len(lower_bound) == len(upper_bound), 'Invalid bounds length'
        lower_bound = np.array(lower_bound)
        upper_bound = np.array(upper_bound)
        assert np.all(upper_bound > lower_bound), 'Invalid boundary values'
        dimensions = len(lower_bound)
        self.particles = self.initialize_particles(lower_bound, upper_bound,
            dimensions, objective_function)
        generation = 1
        while generation <= self.max_generations:
            for particle in self.particles:
                particle.update_velocity(self.omega, self.phip, self.phig,
                    self.best_position[-1])
                particle.update_position(lower_bound, upper_bound,
                    objective_function)
                if particle.best_function_value[-1] == 0:
                    self.retired_particles.append(copy.deepcopy(particle))
                    particle.reset(dimensions, lower_bound, upper_bound,
                        objective_function)
                elif particle.best_function_value[-1
                    ] < self.best_function_value[-1]:
                    stepsize = np.sqrt(np.sum((np.asarray(self.
                        best_position[-1]) - np.asarray(particle.position[-
                        1])) ** 2))
                    if np.abs(np.asarray(self.best_function_value[-1]) - np
                        .asarray(particle.best_function_value[-1])
                        ) <= self.minfunc:
                        return particle.best_position[-1
                            ], particle.best_function_value[-1]
                    elif stepsize <= self.minstep:
                        return particle.best_position[-1
                            ], particle.best_function_value[-1]
                    else:
                        self.best_function_value.append(particle.
                            best_function_value[-1])
                        self.best_position.append(particle.best_position[-1][:]
                            )
            generation += 1
        return self.best_position[-1], self.best_function_value[-1]

    def initialize_particles(self, lower_bound, upper_bound, dimensions,
        objective_function):
        """Initializes the particles for the swarm

		Args:
			objective_function (function): The function to be minimized.
			lower_bound (np.array): Vector of lower boundaries for particle dimensions.
			upper_bound (np.array): Vector of upper boundaries for particle dimensions.
			dimensions (int): Number of dimensions of the search space.

		Returns:
			particles (list): Collection or particles in the swarm

        """
        particles = []
        for _ in range(self.swarmsize):
            particles.append(Particle(lower_bound, upper_bound, dimensions,
                objective_function))
            if particles[-1].best_function_value[-1
                ] < self.best_function_value[-1]:
                self.best_function_value.append(particles[-1].
                    best_function_value[-1])
                self.best_position.append(particles[-1].best_position[-1])
        self.best_position = [self.best_position[-1]]
        self.best_function_value = [self.best_function_value[-1]]
        return particles

# -*- coding: utf-8 -*-

"""PSO module

Copyright (c) 2017 Future Processing sp. z.o.o.

@author: Pablo Ribalta Lorenzo
@email: pribalta@future-processing.com)
@date: 10.04.2017

This module encapsulates all the functionality related to Particle
Swarm Optimization, including the algorithm itself and the Particles
"""

import copy
import numpy as np

class Particle(object):
    """Particle class for PSO

    This class encapsulates the behavior of each particle in PSO and provides
    an efficient way to do bookkeeping about the state of the swarm in any given
    iteration.

    Args:
        lower_bound (np.array): Vector of lower boundaries for particle dimensions.
        upper_bound (np.array): Vector of upper boundaries for particle dimensions.
        dimensions (int): Number of dimensions of the search space.
        objective function (function): Black-box function to evaluate.

    """
    def __init__(self,
                 lower_bound,
                 upper_bound,
                 dimensions,
                 objective_function):
        self.reset(dimensions, lower_bound, upper_bound, objective_function)

    def reset(self,
              dimensions,
              lower_bound,
              upper_bound,
              objective_function):
        """Particle reset

        Allows for reset of a particle without reallocation.

		Args:
			lower_bound (np.array): Vector of lower boundaries for particle dimensions.
			upper_bound (np.array): Vector of upper boundaries for particle dimensions.
			dimensions (int): Number of dimensions of the search space.

        """
        position = []
        for i in range(dimensions):
            if lower_bound[i] < upper_bound[i]:
                position.extend(np.random.randint(lower_bound[i], upper_bound[i] + 1, 1, dtype=int))
            elif lower_bound[i] == upper_bound[i]:
                position.extend(np.array([lower_bound[i]], dtype=int))
            else:
                assert False

        self.position = [position]

        self.velocity = [np.multiply(np.random.rand(dimensions),
                                     (upper_bound - lower_bound)).astype(int)]

        self.best_position = self.position[:]

        self.function_value = [objective_function(self.best_position[-1])]
        self.best_function_value = self.function_value[:]

    def update_velocity(self, omega, phip, phig, best_swarm_position):
        """Particle velocity update

		Args:
			omega (float): Velocity equation constant.
			phip (float): Velocity equation constant.
			phig (float): Velocity equation constant.
			best_swarm_position (np.array): Best particle position.

        """
        random_coefficient_p = np.random.uniform(size=np.asarray(self.position[-1]).shape)
        random_coefficient_g = np.random.uniform(size=np.asarray(self.position[-1]).shape)

        self.velocity.append(omega
                             * np.asarray(self.velocity[-1])
                             + phip
                             * random_coefficient_p
                             * (np.asarray(self.best_position[-1])
                                - np.asarray(self.position[-1]))
                             + phig
                             * random_coefficient_g
                             * (np.asarray(best_swarm_position)
                                - np.asarray(self.position[-1])))

        self.velocity[-1] = self.velocity[-1].astype(int)

    def update_position(self, lower_bound, upper_bound, objective_function):
        """Particle position update

		Args:
			lower_bound (np.array): Vector of lower boundaries for particle dimensions.
			upper_bound (np.array): Vector of upper boundaries for particle dimensions.
			objective function (function): Black-box function to evaluate.

        """
        new_position = self.position[-1] + self.velocity[-1]

        if np.array_equal(self.position[-1], new_position):
            self.function_value.append(self.function_value[-1])
        else:
            mark1 = new_position < lower_bound
            mark2 = new_position > upper_bound

            new_position[mark1] = lower_bound[mark1]
            new_position[mark2] = upper_bound[mark2]

            self.function_value.append(objective_function(self.position[-1]))

        self.position.append(new_position.tolist())

        if self.function_value[-1] < self.best_function_value[-1]:
            self.best_position.append(self.position[-1][:])
            self.best_function_value.append(self.function_value[-1])

class Pso(object):
    """PSO wrapper

    This class contains the particles and provides an abstraction to hold all the context
    of the PSO algorithm

    Args:
        swarmsize (int): Number of particles in the swarm
        maxiter (int): Maximum number of generations the swarm will run

    """
    def __init__(self, swarmsize=100, maxiter=100):
        self.max_generations = maxiter
        self.swarmsize = swarmsize

        self.omega = 0.5
        self.phip = 0.5
        self.phig = 0.5

        self.minstep = 1e-4
        self.minfunc = 1e-4

        self.best_position = [None]
        self.best_function_value = [1]

        self.particles = []

        self.retired_particles = []

    def run(self, function, lower_bound, upper_bound, kwargs=None):
        """Perform a particle swarm optimization (PSO)

		Args:
			objective_function (function): The function to be minimized.
			lower_bound (np.array): Vector of lower boundaries for particle dimensions.
			upper_bound (np.array): Vector of upper boundaries for particle dimensions.

		Returns:
			best_position (np.array): Best known position
			accuracy (float): Objective value at best_position
			:param kwargs:

        """
        if kwargs is None:
            kwargs = {}

        objective_function = lambda x: function(x, **kwargs)
        assert hasattr(function, '__call__'), 'Invalid function handle'

        assert len(lower_bound) == len(upper_bound), 'Invalid bounds length'

        lower_bound = np.array(lower_bound)
        upper_bound = np.array(upper_bound)

        assert np.all(upper_bound > lower_bound), 'Invalid boundary values'


        dimensions = len(lower_bound)

        self.particles = self.initialize_particles(lower_bound,
                                                   upper_bound,
                                                   dimensions,
                                                   objective_function)

        # Start evolution
        generation = 1
        while generation <= self.max_generations:
            for particle in self.particles:
                particle.update_velocity(self.omega, self.phip, self.phig, self.best_position[-1])
                particle.update_position(lower_bound, upper_bound, objective_function)

                if particle.best_function_value[-1] == 0:
                    self.retired_particles.append(copy.deepcopy(particle))
                    particle.reset(dimensions, lower_bound, upper_bound, objective_function)
                elif particle.best_function_value[-1] < self.best_function_value[-1]:
                    stepsize = np.sqrt(np.sum((np.asarray(self.best_position[-1])
                                               - np.asarray(particle.position[-1])) ** 2))

                    if np.abs(np.asarray(self.best_function_value[-1])
                              - np.asarray(particle.best_function_value[-1])) \
                            <= self.minfunc:
                        return particle.best_position[-1], particle.best_function_value[-1]
                    elif stepsize <= self.minstep:
                        return particle.best_position[-1], particle.best_function_value[-1]
                    else:
                        self.best_function_value.append(particle.best_function_value[-1])
                        self.best_position.append(particle.best_position[-1][:])



            generation += 1

        return self.best_position[-1], self.best_function_value[-1]

    def initialize_particles(self,
                             lower_bound,
                             upper_bound,
                             dimensions,
                             objective_function):
        """Initializes the particles for the swarm

		Args:
			objective_function (function): The function to be minimized.
			lower_bound (np.array): Vector of lower boundaries for particle dimensions.
			upper_bound (np.array): Vector of upper boundaries for particle dimensions.
			dimensions (int): Number of dimensions of the search space.

		Returns:
			particles (list): Collection or particles in the swarm

        """
        particles = []
        for _ in range(self.swarmsize):
            particles.append(Particle(lower_bound,
                                      upper_bound,
                                      dimensions,
                                      objective_function))
            if particles[-1].best_function_value[-1] < self.best_function_value[-1]:
                self.best_function_value.append(particles[-1].best_function_value[-1])
                self.best_position.append(particles[-1].best_position[-1])


        self.best_position = [self.best_position[-1]]
        self.best_function_value = [self.best_function_value[-1]]

        return particles