Source code for star_pso.population.particle

from math import inf
from copy import deepcopy

from numpy.typing import ArrayLike
from numpy import copyto as copy_to
from numpy import array, asarray, array_equal

from star_pso.utils import ScalarOrArray

# Public interface.
__all__ = ["Particle"]


[docs] class Particle: """ Description: Models the particle in the swarm of the PSO. """ # Object variables. __slots__ = ("_position", "_value", "_best_position", "_best_value") def __init__(self, initial_position: ArrayLike) -> None: """ Initializes the Particle object with an initial position. :param initial_position: (ArrayLike) initial position. :return: None. """ # Set the initial particle position to a vector. self._position = array(initial_position, copy=True) # Initialize the best (historical) position. self._best_position = array(initial_position, copy=True) # Initialize the best (historical) value to -Inf. self._best_value = -inf # Initially the function value is set to -Inf. self._value = -inf # _end_def_ @property def size(self) -> int: """ Returns the size (or length) of the particle. :return: (int) the length of the particle. """ return len(self._position) # _end_def_ @property def position(self) -> ArrayLike: """ Accessor of the positions in the particle. :return: the ArrayLike (vector) of the positions. """ return self._position # _end_def_ @position.setter def position(self, new_vector: ArrayLike) -> None: """ Updates the position in the particle object. :param new_vector: (ArrayLike) new position vector. :return: None. """ self._position = asarray(new_vector) # _end_def_ @property def best_position(self) -> ArrayLike: """ Returns the best (so far) position of the particle object. :return: (ArrayLike) position vector. """ return self._best_position # _end_def_ @best_position.setter def best_position(self, new_vector: ArrayLike) -> None: """ Updates the best position (so far) in the particle object. :param new_vector: (ArrayLike) new best position vector. :return: None. """ # Note: Since the best position is updated by the # current position we need to copy the new vector # into the best_position vector to avoid pointing # to the same memory twice. copy_to(self._best_position, new_vector) # _end_def_ @property def best_value(self) -> float: """ Accessor of the best function value recorded. :return: (float) best function value. """ return self._best_value # _end_def_ @best_value.setter def best_value(self, new_value: float) -> None: """ Updates the best function value in the particle object. :param new_value: (float) new best function value. :return: None. """ self._best_value = new_value # _end_def_ @property def value(self) -> float: """ Accessor of the current function value. :return: (float) function value. """ return self._value # _end_def_ @value.setter def value(self, new_value: float) -> None: """ Updates the best function value in the particle object. :param new_value: (float) new best function value. :return: None. """ self._value = new_value # _end_def_ def __deepcopy__(self, memo: dict) -> "Particle": """ This custom method overrides the default deepcopy method. :param memo: dictionary of objects already copied during the current copying pass. :return: a new identical "clone" of the self object. """ # Create a new instance. new_object = Particle.__new__(Particle) # Don't copy self reference. memo[id(self)] = new_object # Deep copy the position vector. setattr(new_object, "_position", deepcopy(self._position, memo)) # Deep copy the best position vector. setattr(new_object, "_best_position", deepcopy(self._best_position, memo)) # Simply copy the value (float). setattr(new_object, "_value", self._value) # Simply copy the best value (float). setattr(new_object, "_best_value", self._best_value) # Return an identical particle. return new_object # _end_def_ def __getitem__(self, index: int) -> ScalarOrArray: """ Get the item at position 'index'. :param index: (int) the position that we want to return. :return: the reference to the object in position index. """ return self._position[index] # _end_def_ def __setitem__(self, index: int, item: ScalarOrArray) -> None: """ Set the 'item' at position 'index'. :param index: (int) the position that we want to access. :param item: the object we want to assign in the particle. :return: None. """ self._position[index] = item # _end_def_ def __len__(self) -> int: """ Accessor of the total length of the particle. :return: the length (int) of the particle. """ return len(self._position) # _end_def_ def __eq__(self, other) -> bool: """ Compares the 'self' particle, with the 'other' particle and returns True if they have the same position otherwise False. :param other: particle to compare. :return: True if the positions are identical else False. """ # Check if they are the same instance. if self is other: return True # _end_if_ # Make sure both objects are of the same type 'particle'. if not isinstance(other, Particle): return NotImplemented # _end_if_ # Compare directly their two positional arrays. return array_equal(other.position, self._position) # _end_def_ def __str__(self) -> str: """ Override to print a readable string presentation of the Particle object. :return: a string representation of a Particle. """ return f"{self.__class__.__name__}: "\ f"(Position, Value) = ({self._position, self._value})."
# _end_def_ # _end_class_