class_docstr: Attacks a model with word substiutitions using a Particle Swarm
  Optimization (PSO) algorithm. Some key hyper-parameters are setup according
  to the original paper:
  
  "We adjust PSO on the validation set of SST and set ω_1 as 0.8 and ω_2 as 0.2.
  We set the max velocity of the particles V_{max} to 3, which means the changing
  probability of the particles ranges from 0.047 (sigmoid(-3)) to 0.953 (sigmoid(3))."
  
  Args:
      pop_size (:obj:`int`, optional): The population size. Defaults to 60.
      max_iters (:obj:`int`, optional): The maximum number of iterations to use. Defaults to 20.
      post_turn_check (:obj:`bool`, optional): If `True`, check if new position reached by moving passes the constraints. Defaults to `True`
      max_turn_retries (:obj:`bool`, optional): Maximum number of movement retries if new position after turning fails to pass the constraints.
          Applied only when `post_movement_check` is set to `True`.
          Setting it to 0 means we immediately take the old position as the new position upon failure.
class_docstr_tok: ["Attacks","a","model","with","word","substiutitions","using","a","Particle","Swarm","Optimization","(","PSO",")","algorithm",".","Some","key","hyper-parameters","are","setup","according","to","the","original","paper",":","''","We","adjust","PSO","on","the","validation","set","of","SST","and","set","\u03c9_1","as","0.8","and","\u03c9_2","as","0.2",".","We","set","the","max","velocity","of","the","particles","V_","{","max","}","to","3",",","which","means","the","changing","probability","of","the","particles","ranges","from","0.047","(","sigmoid","(","-3",")",")","to","0.953","(","sigmoid","(","3",")",")",".","''","Args",":","pop_size","(",":","obj",":","`","int","`",",","optional",")",":","The","population","size",".","Defaults","to","60.","max_iters","(",":","obj",":","`","int","`",",","optional",")",":","The","maximum","number","of","iterations","to","use",".","Defaults","to","20.","post_turn_check","(",":","obj",":","`","bool","`",",","optional",")",":","If","`","True","`",",","check","if","new","position","reached","by","moving","passes","the","constraints",".","Defaults","to","`","True","`","max_turn_retries","(",":","obj",":","`","bool","`",",","optional",")",":","Maximum","number","of","movement","retries","if","new","position","after","turning","fails","to","pass","the","constraints",".","Applied","only","when","`","post_movement_check","`","is","set","to","`","True","`",".","Setting","it","to","0","means","we","immediately","take","the","old","position","as","the","new","position","upon","failure","."]
code: def perform_search(self, initial_result):
      self._search_over = False
      population = self._initialize_population(
          initial_result, self.pop_size
      )
      # Initialize  up velocities of each word for each population
      v_init = np.random.uniform(-self.v_max, self.v_max, self.pop_size)
      velocities = np.array(
          [
              [
                  v_init[t]
                  for _ in range(initial_result.attacked_text.num_words)
              ]
              for t in range(self.pop_size)
          ]
      )
  
      global_elite = max(population, key=lambda x: x.score)
      if (
          self._search_over
          or global_elite.result.goal_status
          == GoalFunctionResultStatus.SUCCEEDED
      ):
          return global_elite.result
  
      local_elites = copy.copy(population)
  
      # start iterations
      for i in range(self.max_iters):
          omega = (self.omega_1 - self.omega_2) * (
              self.max_iters - i
          ) / self.max_iters + self.omega_2
          C1 = self.c1_origin - i / self.max_iters * (
              self.c1_origin - self.c2_origin
          )
          C2 = self.c2_origin + i / self.max_iters * (
              self.c1_origin - self.c2_origin
          )
          P1 = C1
          P2 = C2
  
          for k in range(len(population)):
              # calculate the probability of turning each word
              pop_mem_words = population[k].words
              local_elite_words = local_elites[k].words
              assert len(pop_mem_words) == len(
                  local_elite_words
              ), "PSO word length mismatch!"
  
              for d in range(len(pop_mem_words)):
                  velocities[k][d] = omega * velocities[k][d] + (
                      1 - omega
                  ) * (
                      self._equal(
                          pop_mem_words[d], local_elite_words[d]
                      )
                      + self._equal(
                          pop_mem_words[d], global_elite.words[d]
                      )
                  )
              turn_prob = utils.sigmoid(velocities[k])
  
              if np.random.uniform() < P1:
                  # Move towards local elite
                  population[k] = self._turn(
                      local_elites[k],
                      population[k],
                      turn_prob,
                      initial_result.attacked_text,
                  )
  
              if np.random.uniform() < P2:
                  # Move towards global elite
                  population[k] = self._turn(
                      global_elite,
                      population[k],
                      turn_prob,
                      initial_result.attacked_text,
                  )
  
          # Check if there is any successful attack in the current population
          pop_results, self._search_over = self.get_goal_results(
              [p.attacked_text for p in population]
          )
          if self._search_over:
              # if `get_goal_results` gets cut short by query budget, resize population
              population = population[: len(pop_results)]
          for k in range(len(pop_results)):
              population[k].result = pop_results[k]
  
          top_member = max(population, key=lambda x: x.score)
          if (
              self._search_over
              or top_member.result.goal_status
              == GoalFunctionResultStatus.SUCCEEDED
          ):
              return top_member.result
  
          # Mutation based on the current change rate
          for k in range(len(population)):
              change_ratio = (
                  initial_result.attacked_text.words_diff_ratio(
                      population[k].attacked_text
                  )
              )
              # Referred from the original source code
              p_change = 1 - 2 * change_ratio
              if np.random.uniform() < p_change:
                  self._perturb(population[k], initial_result)
  
              if self._search_over:
                  break
  
          # Check if there is any successful attack in the current population
          top_member = max(population, key=lambda x: x.score)
          if (
              self._search_over
              or top_member.result.goal_status
              == GoalFunctionResultStatus.SUCCEEDED
          ):
              return top_member.result
  
          # Update the elite if the score is increased
          for k in range(len(population)):
              if population[k].score > local_elites[k].score:
                  local_elites[k] = copy.copy(population[k])
  
          if top_member.score > global_elite.score:
              global_elite = copy.copy(top_member)
  
      return global_elite.result
  
file_path: TextAttack/textattack/search_methods/particle_swarm_optimization.py
imports: import copy
  import numpy
  from textattack.goal_function_results import GoalFunctionResultStatus
  from textattack.search_methods import PopulationBasedSearch, PopulationMember
  from textattack.shared import utils
  from textattack.shared.validators import transformation_consists_of_word_swaps
label_desc: Use image node_id 7 for calling the ParticleSwarmOptimization obj's underlying member method code with example usage: obj.perform_search(initial_result) and returns: global_elite, global_elite, top_member, top_member