






import math


class 		lowerCAmelCase_							:





     def __init__(  self       ,   _UpperCAmelCase       ,   _UpperCAmelCase       ,   _UpperCAmelCase       ,   _UpperCAmelCase     )		->							int:
        lowerCamelCase											=x
        lowerCamelCase											=y
        lowerCamelCase											=step_size
        lowerCamelCase											=function_to_optimize





     def 	lowerCamelCase       (  self     )		->							int:
        return self.function(self.x       ,   self.y     )





     def 	lowerCamelCase       (  self     )		->							Union[str, Any]:
        lowerCamelCase											=self.step_size
        return [
            SearchProblem(_UpperCAmelCase       ,   _UpperCAmelCase       ,   _UpperCAmelCase       ,   self.function     )
            for x, y in (
                (self.x - step_size, self.y - step_size),
                (self.x - step_size, self.y),
                (self.x - step_size, self.y + step_size),
                (self.x, self.y - step_size),
                (self.x, self.y + step_size),
                (self.x + step_size, self.y - step_size),
                (self.x + step_size, self.y),
                (self.x + step_size, self.y + step_size),
            )
        ]





     def __hash__(  self     )		->							Tuple:
        return hash(str(self     )     )





     def __eq__(  self       ,   _UpperCAmelCase     )		->							Union[str, Any]:
        if isinstance(_UpperCAmelCase       ,   _UpperCAmelCase     ):
           return hash(str(self     )     ) == hash(str(_UpperCAmelCase     )     )
        return False





     def __str__(  self     )		->							int:
        return F'''x: {self.x} y: {self.y}'''






def    UpperCAmelCase__	(						lowerCamelCase  : Union[str, Any] ,lowerCamelCase  : bool = True ,lowerCamelCase  : float = math.inf ,lowerCamelCase  : float = -math.inf ,lowerCamelCase  : float = math.inf ,lowerCamelCase  : float = -math.inf ,lowerCamelCase  : bool = False ,lowerCamelCase  : int = 1_00_00 ,):
   lowerCamelCase											=search_prob
   lowerCamelCase											=[]  # list to store the current score at each iteration
   lowerCamelCase											=0
   lowerCamelCase											=False
   lowerCamelCase											=set()
   while not solution_found and iterations < max_iter:
      visited.add(lowerCamelCase						)
      iterations += 1
      lowerCamelCase											=current_state.score()
      scores.append(lowerCamelCase						)
      lowerCamelCase											=current_state.get_neighbors()
      lowerCamelCase											=-math.inf
      lowerCamelCase											=math.inf
      lowerCamelCase											=None  # to hold the next best neighbor
      for neighbor in neighbors:
         if neighbor in visited:
            continue  # do not want to visit the same state again
         if (
             neighbor.x > max_x
             or neighbor.x < min_x
             or neighbor.y > max_y
             or neighbor.y < min_y
         ):
            continue  # neighbor outside our bounds
         lowerCamelCase											=neighbor.score() - current_score
         if find_max:  # finding max
            # going to direction with greatest ascent
            if change > max_change and change > 0:
               lowerCamelCase											=change
               lowerCamelCase											=neighbor
         else:  # finding min
            # to direction with greatest descent
            if change < min_change and change < 0:
               lowerCamelCase											=change
               lowerCamelCase											=neighbor
      if next_state is not None:
         # we found at least one neighbor which improved the current state
         lowerCamelCase											=next_state
      else:
         # since we have no neighbor that improves the solution we stop the search
         lowerCamelCase											=True

   if visualization:
      from matplotlib import pyplot as plt

      plt.plot(range(lowerCamelCase						) ,lowerCamelCase						)
      plt.xlabel("""Iterations"""						)
      plt.ylabel("""Function values"""						)
      plt.show()

   return current_state


if __name__ == "__main__":
   import doctest

   doctest.testmod()






   def    UpperCAmelCase__	(						lowerCamelCase  : str ,lowerCamelCase  : str						):
      return (x**2) + (y**2)

   # starting the problem with initial coordinates (3, 4)
   _UpperCAmelCase				:	Optional[Any]    						=    SearchProblem(x=3, y=4, step_size=1, function_to_optimize=test_fa)
   _UpperCAmelCase				:	Tuple    						=    hill_climbing(prob, find_max=False)
   print(
       'The minimum score for f(x, y) = x^2 + y^2 found via hill climbing: '
       f"{local_min.score()}"
   )

   # starting the problem with initial coordinates (12, 47)
   _UpperCAmelCase				:	Optional[int]    						=    SearchProblem(x=12, y=47, step_size=1, function_to_optimize=test_fa)
   _UpperCAmelCase				:	Any    						=    hill_climbing(
       prob, find_max=False, max_x=100, min_x=5, max_y=50, min_y=-5, visualization=True
   )
   print(
       'The minimum score for f(x, y) = x^2 + y^2 with the domain 100 > x > 5 '
       f"and 50 > y > - 5 found via hill climbing: {local_min.score()}"
   )






   def    UpperCAmelCase__	(						lowerCamelCase  : str ,lowerCamelCase  : Optional[int]						):
      return (3 * x**2) - (6 * y)

   _UpperCAmelCase				:	int    						=    SearchProblem(x=3, y=4, step_size=1, function_to_optimize=test_fa)
   _UpperCAmelCase				:	Optional[int]    						=    hill_climbing(prob, find_max=True)







   print(
       'The maximum score for f(x, y) = x^2 + y^2 found via hill climbing: '
       f"{local_min.score()}"
   )







from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier

# Load iris file
_UpperCAmelCase				:	Optional[int]    						=    load_iris()
iris.keys()


print(f"Target names: \n {iris.target_names} ")
print(f"\n Features: \n {iris.feature_names}")

# Train set e Test set
_UpperCAmelCase						,						_UpperCAmelCase						,						_UpperCAmelCase						,						_UpperCAmelCase				:	List[Any]    						=    train_test_split(
    iris['data'], iris['target'], random_state=4
)

# KNN

_UpperCAmelCase				:	Optional[int]    						=    KNeighborsClassifier(n_neighbors=1)
knn.fit(X_train, y_train)

# new array to test
_UpperCAmelCase				:	List[Any]    						=    [[1, 2, 1, 4], [2, 3, 4, 5]]

_UpperCAmelCase				:	Tuple    						=    knn.predict(X_new)

print(
    f"\nNew array: \n {X_new}\n\nTarget Names Prediction: \n"
    f" {iris['target_names'][prediction]}"
)