diff --git a/src/mlrose_ky/__init__.py b/src/mlrose_ky/__init__.py index bb124c3e..a6b36e10 100644 --- a/src/mlrose_ky/__init__.py +++ b/src/mlrose_ky/__init__.py @@ -1,4 +1,4 @@ -"""MLROSe initialization file.""" +"""mlrose-ky initialization file.""" # Authors: Genevieve Hayes (modified by Andrew Rollings, Kyle Nakamura) # License: BSD 3-clause diff --git a/src/mlrose_ky/algorithms/crossovers/_crossover_base.py b/src/mlrose_ky/algorithms/crossovers/_crossover_base.py index 2c8f270d..5ea36553 100644 --- a/src/mlrose_ky/algorithms/crossovers/_crossover_base.py +++ b/src/mlrose_ky/algorithms/crossovers/_crossover_base.py @@ -34,7 +34,7 @@ class _CrossoverBase(ABC): 'length' property. """ - def __init__(self, optimization_problem: Any) -> None: + def __init__(self, optimization_problem: Any): """ Initialize the CrossoverBase with the given optimization problem. diff --git a/src/mlrose_ky/algorithms/crossovers/one_point_crossover.py b/src/mlrose_ky/algorithms/crossovers/one_point_crossover.py index eaf031cf..ab498edf 100644 --- a/src/mlrose_ky/algorithms/crossovers/one_point_crossover.py +++ b/src/mlrose_ky/algorithms/crossovers/one_point_crossover.py @@ -26,7 +26,7 @@ class OnePointCrossover(_CrossoverBase): _CrossoverBase : Abstract base class for crossover operations. """ - def __init__(self, optimization_problem: Any) -> None: + def __init__(self, optimization_problem: Any): """ Initialize the OnePointCrossover with the given optimization problem. diff --git a/src/mlrose_ky/algorithms/crossovers/tsp_crossover.py b/src/mlrose_ky/algorithms/crossovers/tsp_crossover.py index 7ba275b9..e29b9c5f 100644 --- a/src/mlrose_ky/algorithms/crossovers/tsp_crossover.py +++ b/src/mlrose_ky/algorithms/crossovers/tsp_crossover.py @@ -27,7 +27,7 @@ class TSPCrossover(_CrossoverBase): _CrossoverBase : Abstract base class for crossover operations. """ - def __init__(self, optimization_problem: Any) -> None: + def __init__(self, optimization_problem: Any): """ Initialize the TSPCrossover with the given optimization problem. diff --git a/src/mlrose_ky/algorithms/crossovers/uniform_crossover.py b/src/mlrose_ky/algorithms/crossovers/uniform_crossover.py index 573dbf15..5c7f247e 100644 --- a/src/mlrose_ky/algorithms/crossovers/uniform_crossover.py +++ b/src/mlrose_ky/algorithms/crossovers/uniform_crossover.py @@ -27,7 +27,7 @@ class UniformCrossover(_CrossoverBase): _CrossoverBase : Abstract base class for crossover operations. """ - def __init__(self, optimization_problem: Any) -> None: + def __init__(self, optimization_problem: Any): """ Initialize the UniformCrossover with the given optimization problem. diff --git a/src/mlrose_ky/algorithms/decay/arithmetic_decay.py b/src/mlrose_ky/algorithms/decay/arithmetic_decay.py index fcc64159..433fd5b8 100644 --- a/src/mlrose_ky/algorithms/decay/arithmetic_decay.py +++ b/src/mlrose_ky/algorithms/decay/arithmetic_decay.py @@ -41,7 +41,7 @@ class ArithDecay: 5.25 """ - def __init__(self, init_temp: float = 1.0, decay: float = 0.0001, min_temp: float = 0.001) -> None: + def __init__(self, init_temp: float = 1.0, decay: float = 0.0001, min_temp: float = 0.001): self.init_temp: float = init_temp self.decay: float = decay self.min_temp: float = min_temp diff --git a/src/mlrose_ky/algorithms/decay/custom_decay.py b/src/mlrose_ky/algorithms/decay/custom_decay.py index 772faabd..35fcc064 100644 --- a/src/mlrose_ky/algorithms/decay/custom_decay.py +++ b/src/mlrose_ky/algorithms/decay/custom_decay.py @@ -29,7 +29,7 @@ class CustomSchedule: 15 """ - def __init__(self, schedule: Callable[..., float], **kwargs) -> None: + def __init__(self, schedule: Callable[..., float], **kwargs): self.schedule: Callable[..., float] = schedule self.kwargs: dict = kwargs diff --git a/src/mlrose_ky/algorithms/decay/exponential_decay.py b/src/mlrose_ky/algorithms/decay/exponential_decay.py index ff24d114..65f48c86 100644 --- a/src/mlrose_ky/algorithms/decay/exponential_decay.py +++ b/src/mlrose_ky/algorithms/decay/exponential_decay.py @@ -43,7 +43,7 @@ class ExpDecay: 7.788007830714049 """ - def __init__(self, init_temp: float = 1.0, exp_const: float = 0.005, min_temp: float = 0.001) -> None: + def __init__(self, init_temp: float = 1.0, exp_const: float = 0.005, min_temp: float = 0.001): self.init_temp: float = init_temp self.exp_const: float = exp_const self.min_temp: float = min_temp diff --git a/src/mlrose_ky/algorithms/decay/geometric_decay.py b/src/mlrose_ky/algorithms/decay/geometric_decay.py index 2fb1bb25..218196e1 100644 --- a/src/mlrose_ky/algorithms/decay/geometric_decay.py +++ b/src/mlrose_ky/algorithms/decay/geometric_decay.py @@ -41,7 +41,7 @@ class GeomDecay: 7.737809374999998 """ - def __init__(self, init_temp: float = 1.0, decay: float = 0.99, min_temp: float = 0.001) -> None: + def __init__(self, init_temp: float = 1.0, decay: float = 0.99, min_temp: float = 0.001): self.init_temp: float = init_temp self.decay: float = decay self.min_temp: float = min_temp diff --git a/src/mlrose_ky/algorithms/mutators/_mutator_base.py b/src/mlrose_ky/algorithms/mutators/_mutator_base.py index fd7c4a03..9b3aaeec 100644 --- a/src/mlrose_ky/algorithms/mutators/_mutator_base.py +++ b/src/mlrose_ky/algorithms/mutators/_mutator_base.py @@ -33,8 +33,9 @@ class _MutatorBase(ABC): An instance of an optimization problem that the mutator will operate on. """ - def __init__(self, opt_prob: Any) -> None: + def __init__(self, opt_prob: Any): super().__init__() + self._opt_prob: Any = opt_prob self._length: int = opt_prob.length diff --git a/src/mlrose_ky/algorithms/mutators/discrete_mutator.py b/src/mlrose_ky/algorithms/mutators/discrete_mutator.py index e5ed35a4..87f0d89a 100644 --- a/src/mlrose_ky/algorithms/mutators/discrete_mutator.py +++ b/src/mlrose_ky/algorithms/mutators/discrete_mutator.py @@ -30,8 +30,9 @@ class DiscreteMutator(_MutatorBase): An instance of an optimization problem that the mutator will operate on. """ - def __init__(self, opt_prob: Any) -> None: + def __init__(self, opt_prob: Any): super().__init__(opt_prob) + self._max_val: int = opt_prob.max_val def mutate(self, child: np.ndarray, mutation_probability: float) -> np.ndarray: diff --git a/src/mlrose_ky/algorithms/mutators/gene_swap_mutator.py b/src/mlrose_ky/algorithms/mutators/gene_swap_mutator.py index c7a9e56e..ae5c9add 100644 --- a/src/mlrose_ky/algorithms/mutators/gene_swap_mutator.py +++ b/src/mlrose_ky/algorithms/mutators/gene_swap_mutator.py @@ -28,7 +28,7 @@ class SwapMutator(_MutatorBase): An instance of an optimization problem that the mutator will operate on. """ - def __init__(self, opt_prob: Any) -> None: + def __init__(self, opt_prob: Any): super().__init__(opt_prob) def mutate(self, child: np.ndarray, mutation_probability: float) -> np.ndarray: diff --git a/src/mlrose_ky/algorithms/mutators/single_gene_mutator.py b/src/mlrose_ky/algorithms/mutators/single_gene_mutator.py index 55f76f04..031dc7f1 100644 --- a/src/mlrose_ky/algorithms/mutators/single_gene_mutator.py +++ b/src/mlrose_ky/algorithms/mutators/single_gene_mutator.py @@ -29,8 +29,9 @@ class ChangeOneMutator(_MutatorBase): An instance of an optimization problem that the mutator will operate on. """ - def __init__(self, opt_prob: Any) -> None: + def __init__(self, opt_prob: Any): super().__init__(opt_prob) + self._max_val: int = opt_prob.max_val def mutate(self, child: np.ndarray, mutation_probability: float) -> np.ndarray: diff --git a/src/mlrose_ky/algorithms/mutators/single_shift_mutator.py b/src/mlrose_ky/algorithms/mutators/single_shift_mutator.py index d36d55b6..4657fe33 100644 --- a/src/mlrose_ky/algorithms/mutators/single_shift_mutator.py +++ b/src/mlrose_ky/algorithms/mutators/single_shift_mutator.py @@ -30,8 +30,9 @@ class ShiftOneMutator(_MutatorBase): An instance of an optimization problem that the mutator will operate on. """ - def __init__(self, opt_prob: Any) -> None: + def __init__(self, opt_prob: Any): super().__init__(opt_prob) + self._max_val: int = opt_prob.max_val def mutate(self, child: np.ndarray, mutation_probability: float) -> np.ndarray: diff --git a/src/mlrose_ky/fitness/continuous_peaks.py b/src/mlrose_ky/fitness/continuous_peaks.py index f5ff837d..c1eb91fc 100644 --- a/src/mlrose_ky/fitness/continuous_peaks.py +++ b/src/mlrose_ky/fitness/continuous_peaks.py @@ -37,7 +37,7 @@ class ContinuousPeaks: with `max_val = 2`) optimization problems only. """ - def __init__(self, t_pct: float = 0.1) -> None: + def __init__(self, t_pct: float = 0.1): self.t_pct: float = t_pct self.prob_type: str = "discrete" diff --git a/src/mlrose_ky/fitness/max_k_color.py b/src/mlrose_ky/fitness/max_k_color.py index fedd8b18..a7b36f49 100644 --- a/src/mlrose_ky/fitness/max_k_color.py +++ b/src/mlrose_ky/fitness/max_k_color.py @@ -110,7 +110,7 @@ def get_prob_type(self) -> str: """ return self.prob_type - def set_graph(self, graph) -> None: + def set_graph(self, graph): """Set the graph edges from an external graph representation. Parameters diff --git a/src/mlrose_ky/fitness/one_max.py b/src/mlrose_ky/fitness/one_max.py index 51c1bae5..b708e478 100644 --- a/src/mlrose_ky/fitness/one_max.py +++ b/src/mlrose_ky/fitness/one_max.py @@ -27,7 +27,7 @@ class OneMax: The One Max fitness function is suitable for use in either discrete or continuous-state optimization problems. """ - def __init__(self) -> None: + def __init__(self): self.prob_type: str = "either" @staticmethod diff --git a/src/mlrose_ky/fitness/travelling_salesperson.py b/src/mlrose_ky/fitness/travelling_salesperson.py index 7f3f572e..f275fec0 100644 --- a/src/mlrose_ky/fitness/travelling_salesperson.py +++ b/src/mlrose_ky/fitness/travelling_salesperson.py @@ -51,7 +51,7 @@ class TravellingSalesperson: fitness function object. """ - def __init__(self, coords: list[tuple] = None, distances: list[tuple] = None) -> None: + def __init__(self, coords: list[tuple] = None, distances: list[tuple] = None): # Ensure that at least one of coords or distances is provided if coords is None and distances is None: raise ValueError("At least one of coords and distances must be specified.") diff --git a/src/mlrose_ky/gridsearch/grid_search_mixin.py b/src/mlrose_ky/gridsearch/grid_search_mixin.py index 7219aaee..b05e6a65 100644 --- a/src/mlrose_ky/gridsearch/grid_search_mixin.py +++ b/src/mlrose_ky/gridsearch/grid_search_mixin.py @@ -27,7 +27,7 @@ class GridSearchMixin: Flag indicating whether to apply argmax to predictions and ground truths before scoring. """ - def __init__(self, scorer_method: Callable = None) -> None: + def __init__(self, scorer_method: Callable = None): """ Initializes the GridSearchMixin with a specified scoring method. diff --git a/src/mlrose_ky/neural/_nn_base.py b/src/mlrose_ky/neural/_nn_base.py index f28a1c5a..0bef1422 100644 --- a/src/mlrose_ky/neural/_nn_base.py +++ b/src/mlrose_ky/neural/_nn_base.py @@ -27,7 +27,7 @@ def __init__(self): pass @abstractmethod - def fit(self, X: np.ndarray, y: np.ndarray = None, init_weights: np.ndarray = None) -> None: + def fit(self, X: np.ndarray, y: np.ndarray = None, init_weights: np.ndarray = None): """ Fit the neural network to the data. diff --git a/src/mlrose_ky/neural/_nn_core.py b/src/mlrose_ky/neural/_nn_core.py index c8fdb652..181e5e38 100644 --- a/src/mlrose_ky/neural/_nn_core.py +++ b/src/mlrose_ky/neural/_nn_core.py @@ -8,6 +8,7 @@ import numpy as np from sklearn.preprocessing import LabelBinarizer + from mlrose_ky.algorithms.decay import GeomDecay from mlrose_ky.algorithms.ga import genetic_alg from mlrose_ky.algorithms.rhc import random_hill_climb @@ -71,7 +72,7 @@ def __init__( self.predicted_probs: np.ndarray = np.array([]) self.fitness_curve: list[float] = [] - def _validate(self) -> None: + def _validate(self): """Validate the model parameters.""" if (not isinstance(self.max_iters, int) and self.max_iters != np.inf) or self.max_iters < 0: raise ValueError(f"max_iters must be a positive integer, got {self.max_iters}.") @@ -109,7 +110,7 @@ def _validate(self) -> None: f"'random_hill_climb', 'simulated_annealing', 'genetic_alg', 'gradient_descent', got {self.algorithm}." ) - def _validate_input(self, y: np.ndarray) -> None: + def _validate_input(self, y: np.ndarray): """ Add classes_ attribute based on classes present in y. diff --git a/src/mlrose_ky/neural/activation/identity.py b/src/mlrose_ky/neural/activation/identity.py index 77d7e78c..167083a2 100644 --- a/src/mlrose_ky/neural/activation/identity.py +++ b/src/mlrose_ky/neural/activation/identity.py @@ -4,6 +4,7 @@ # License: BSD 3-clause import warnings + import numpy as np from mlrose_ky.decorators import short_name diff --git a/src/mlrose_ky/neural/linear_regression.py b/src/mlrose_ky/neural/linear_regression.py index de28d4b9..df323962 100644 --- a/src/mlrose_ky/neural/linear_regression.py +++ b/src/mlrose_ky/neural/linear_regression.py @@ -4,6 +4,7 @@ # License: BSD 3-clause from sklearn.base import RegressorMixin + from mlrose_ky.algorithms.decay import GeomDecay from ._nn_core import _NNCore diff --git a/src/mlrose_ky/neural/nn_classifier.py b/src/mlrose_ky/neural/nn_classifier.py index c5a72727..f192f49f 100644 --- a/src/mlrose_ky/neural/nn_classifier.py +++ b/src/mlrose_ky/neural/nn_classifier.py @@ -3,9 +3,11 @@ # Authors: Genevieve Hayes (modified by Andrew Rollings, Kyle Nakamura) # License: BSD 3-clause +from typing import Any, Optional, Callable + import numpy as np + from mlrose_ky.neural._nn_base import _NNBase -from typing import Any, Optional, Callable class NNClassifier(_NNBase): diff --git a/src/mlrose_ky/opt_probs/continuous_opt.py b/src/mlrose_ky/opt_probs/continuous_opt.py index 75ef8285..f95bf0c1 100644 --- a/src/mlrose_ky/opt_probs/continuous_opt.py +++ b/src/mlrose_ky/opt_probs/continuous_opt.py @@ -2,14 +2,15 @@ from typing import Any -# Authors: Genevieve Hayes (modified by Andrew Rollings, Kyle Nakamura) -# License: BSD 3-clause - import numpy as np from mlrose_ky.opt_probs.opt_prob import _OptProb +# Authors: Genevieve Hayes (modified by Andrew Rollings, Kyle Nakamura) +# License: BSD 3-clause + + class ContinuousOpt(_OptProb): """Class for defining continuous-state optimization problems. @@ -77,7 +78,7 @@ def calculate_updates(self) -> list: """ return self.fitness_fn.calculate_updates() - def find_neighbors(self) -> None: + def find_neighbors(self): """Find all neighbors of the current state.""" # Pre-allocate a NumPy array for neighbors (maximum of 2 * length neighbors) neighbors_matrix = np.zeros((2 * self.length, self.length)) @@ -145,7 +146,7 @@ def random_neighbor(self) -> np.ndarray: return neighbor - def random_pop(self, pop_size: int) -> None: + def random_pop(self, pop_size: int): """Create a population of random state vectors. Parameters @@ -221,7 +222,7 @@ def reproduce(self, parent_1: np.ndarray, parent_2: np.ndarray, mutation_prob: f return child - def reset(self) -> None: + def reset(self): """Set the current state vector to a random value and reset its fitness.""" self.state = self.random() self.fitness_evaluations = 0 diff --git a/src/mlrose_ky/opt_probs/discrete_opt.py b/src/mlrose_ky/opt_probs/discrete_opt.py index e7227e10..153b2362 100644 --- a/src/mlrose_ky/opt_probs/discrete_opt.py +++ b/src/mlrose_ky/opt_probs/discrete_opt.py @@ -106,7 +106,7 @@ def __init__( self._mut_mask: np.ndarray | None = None self._mut_inf: np.ndarray | None = None - def eval_node_probs(self) -> None: + def eval_node_probs(self): """Update probability density estimates.""" mutual_info = self._get_mutual_info_impl() @@ -143,7 +143,7 @@ def eval_node_probs(self) -> None: self.node_probs = probs self.parent_nodes = parent - def set_mimic_fast_mode(self, fast_mode: bool) -> None: + def set_mimic_fast_mode(self, fast_mode: bool): """Enable or disable MIMIC fast mode.""" if fast_mode: mut_mask = np.zeros([self.length, self.length], dtype=bool) @@ -214,7 +214,7 @@ def _get_mutual_info_fast(self) -> np.ndarray: return mutual_info - def find_neighbors(self) -> None: + def find_neighbors(self): """Find all neighbors of the current state.""" self.neighbors = [] @@ -233,7 +233,7 @@ def find_neighbors(self) -> None: neighbor[i] = j self.neighbors.append(neighbor) - def find_sample_order(self) -> None: + def find_sample_order(self): """Determine order in which to generate sample vector elements.""" sample_order = [] last = [0] @@ -253,7 +253,7 @@ def find_sample_order(self) -> None: self.sample_order = sample_order - def find_top_pct(self, keep_pct: float) -> None: + def find_top_pct(self, keep_pct: float): """Select samples with fitness in the top keep_pct percentile. Parameters @@ -318,7 +318,7 @@ def random_neighbor(self) -> np.ndarray: return neighbor - def random_pop(self, pop_size: int) -> None: + def random_pop(self, pop_size: int): """Create a population of random state vectors. Parameters @@ -367,7 +367,7 @@ def reproduce(self, parent_1: np.ndarray, parent_2: np.ndarray, mutation_prob: f child = self._crossover.mate(parent_1, parent_2) return self._mutator.mutate(child, mutation_prob) - def reset(self) -> None: + def reset(self): """Set the current state vector to a random value and get its fitness.""" self.state = self.random() self.fitness = self.eval_fitness(self.state) diff --git a/src/mlrose_ky/opt_probs/flip_flop_opt.py b/src/mlrose_ky/opt_probs/flip_flop_opt.py index 319a3882..1fe4a07d 100644 --- a/src/mlrose_ky/opt_probs/flip_flop_opt.py +++ b/src/mlrose_ky/opt_probs/flip_flop_opt.py @@ -80,11 +80,11 @@ def __init__( state = np.random.randint(2, size=self.length) self.set_state(state) - def evaluate_population_fitness(self) -> None: + def evaluate_population_fitness(self): """Calculate fitness for the current population.""" self.pop_fitness = self.fitness_fn.evaluate_many(self.population) - def random_pop(self, pop_size: int) -> None: + def random_pop(self, pop_size: int): """Create a population of random state vectors. Parameters diff --git a/src/mlrose_ky/opt_probs/opt_prob.py b/src/mlrose_ky/opt_probs/opt_prob.py index b904d036..550063ee 100644 --- a/src/mlrose_ky/opt_probs/opt_prob.py +++ b/src/mlrose_ky/opt_probs/opt_prob.py @@ -113,7 +113,7 @@ def eval_fitness(self, state: np.ndarray) -> float: self.fitness_evaluations += 1 return fitness - def eval_mate_probs(self) -> None: + def eval_mate_probs(self): """Calculate the probability of each member of the population reproducing.""" pop_fitness = np.copy(self.pop_fitness) @@ -210,7 +210,7 @@ def get_state(self) -> np.ndarray: """ return self.state - def set_population(self, new_population: np.ndarray) -> None: + def set_population(self, new_population: np.ndarray): """Set a new population and evaluate its fitness. Parameters @@ -221,11 +221,11 @@ def set_population(self, new_population: np.ndarray) -> None: self.population = new_population self.evaluate_population_fitness() - def evaluate_population_fitness(self) -> None: + def evaluate_population_fitness(self): """Evaluate the fitness of the current population.""" self.pop_fitness = np.array([self.eval_fitness(indiv) for indiv in self.population]) - def set_state(self, new_state: np.ndarray) -> None: + def set_state(self, new_state: np.ndarray): """Set a new state vector and evaluate its fitness. Parameters diff --git a/src/mlrose_ky/opt_probs/queens_opt.py b/src/mlrose_ky/opt_probs/queens_opt.py index f5c776ad..56a1d2cf 100644 --- a/src/mlrose_ky/opt_probs/queens_opt.py +++ b/src/mlrose_ky/opt_probs/queens_opt.py @@ -4,6 +4,7 @@ # License: BSD 3-clause from typing import Any + import numpy as np from mlrose_ky.algorithms.crossovers import UniformCrossover diff --git a/src/mlrose_ky/opt_probs/tsp_opt.py b/src/mlrose_ky/opt_probs/tsp_opt.py index eb496cff..eae0e4e9 100644 --- a/src/mlrose_ky/opt_probs/tsp_opt.py +++ b/src/mlrose_ky/opt_probs/tsp_opt.py @@ -96,7 +96,7 @@ def adjust_probs(probs: np.ndarray) -> np.ndarray: sp = np.sum(probs) return np.zeros(np.shape(probs)) if sp == 0 else probs / sp - def find_neighbors(self) -> None: + def find_neighbors(self): """Find all neighbors of the current state.""" self.neighbors = [] diff --git a/src/mlrose_ky/runners/_nn_runner_base.py b/src/mlrose_ky/runners/_nn_runner_base.py index 08db741e..e1879ee3 100644 --- a/src/mlrose_ky/runners/_nn_runner_base.py +++ b/src/mlrose_ky/runners/_nn_runner_base.py @@ -83,7 +83,7 @@ def __init__( n_jobs: int = 1, replay: bool = False, **kwargs, - ) -> None: + ): """ Initializes the _NNRunnerBase class. @@ -272,7 +272,7 @@ def _check_match(df_reference: pd.DataFrame, df_to_check: pd.DataFrame) -> bool: return False - def _tear_down(self, filename: str | None = None) -> None: + def _tear_down(self, filename: str | None = None): """ Finalizes the runner, ensuring that the proper files are saved or cleaned up. diff --git a/src/mlrose_ky/runners/_runner_base.py b/src/mlrose_ky/runners/_runner_base.py index ec3d6b75..65e934b2 100644 --- a/src/mlrose_ky/runners/_runner_base.py +++ b/src/mlrose_ky/runners/_runner_base.py @@ -66,12 +66,12 @@ def get_dynamic_runner_name(self) -> str: return dynamic_runner_name - def set_dynamic_runner_name(self, name: str) -> None: + def set_dynamic_runner_name(self, name: str): """Set a dynamic runner name.""" self._dynamic_short_name = name @staticmethod - def _print_banner(text: str) -> None: + def _print_banner(text: str): """Print a formatted banner for logging.""" logging.info("*" * len(text)) logging.info(text) @@ -92,7 +92,7 @@ def _sanitize_value(value: Any) -> str: return sanitized_value @abstractmethod - def run(self) -> None: + def run(self): """Abstract method to be implemented by subclasses.""" pass @@ -109,7 +109,7 @@ def __init__( replay: bool = False, override_ctrl_c_handler: bool = True, **kwargs: Any, - ) -> None: + ): """ Initialize the runner with required parameters, including problem setup, iteration controls, and output settings. @@ -169,12 +169,12 @@ def __init__( self._increment_spawn_count() - def _increment_spawn_count(self) -> None: + def _increment_spawn_count(self): """Increment the spawn count for the runner (used in parallel execution).""" with self._spawn_count.get_lock(): self._spawn_count.value += 1 - def _decrement_spawn_count(self) -> None: + def _decrement_spawn_count(self): """Decrement the spawn count for the runner.""" with self._spawn_count.get_lock(): self._spawn_count.value -= 1 @@ -184,7 +184,7 @@ def get_spawn_count(self) -> int: self._print_banner(f"*** Spawn Count Remaining: {self._spawn_count.value} ***") return self._spawn_count.value - def abort(self) -> None: + def abort(self): """Set the abort flag to signal that the runner should stop execution.""" self._print_banner("*** ABORTING ***") @@ -195,7 +195,7 @@ def has_aborted(self) -> bool: """Return whether the abort flag has been set.""" return self._abort_flag.value - def set_replay_mode(self, value: bool = True) -> None: + def set_replay_mode(self, value: bool = True): """Enable or disable replay mode, which reuses previous results.""" with self._replay_mode.get_lock(): self._replay_mode.value = value @@ -204,7 +204,7 @@ def replay_mode(self) -> bool: """Check if replay mode is enabled.""" return self._replay_mode.value - def _setup(self) -> None: + def _setup(self): """Prepare the runner by clearing stats, setting up directories, and handling Ctrl-C interrupts.""" self._raw_run_stats = [] self._fitness_curves = [] @@ -225,7 +225,7 @@ def _setup(self) -> None: self._original_sigint_handler = signal.getsignal(signal.SIGINT) signal.signal(signal.SIGINT, self._ctrl_c_handler) - def _ctrl_c_handler(self, sig: int, frame: Any) -> None: + def _ctrl_c_handler(self, sig: int, frame: Any): """ Handle Ctrl-C interruptions by saving progress and aborting the run. @@ -240,7 +240,7 @@ def _ctrl_c_handler(self, sig: int, frame: Any) -> None: self._sigint_params = (sig, frame) self.abort() - def _tear_down(self) -> None: + def _tear_down(self): """Clean up after the experiment, restoring signal handlers and managing resources.""" if not self.override_ctrl_c_handler: return @@ -256,7 +256,7 @@ def _tear_down(self) -> None: except (ValueError, TypeError, AttributeError, Exception) as e: logging.error(f"Problem restoring SIGINT handler: {e}") - def log_current_argument(self, arg_name: str, arg_value: Any) -> None: + def log_current_argument(self, arg_name: str, arg_value: Any): """Log the current argument passed to the algorithm.""" self._current_logged_algorithm_args[arg_name] = arg_value @@ -304,7 +304,7 @@ def run_experiment(self, algorithm: Any, **kwargs: Any) -> tuple[pd.DataFrame | return self.run_stats_df, self.curves_df - def _run_one_experiment(self, algorithm: Any, total_args: dict[str, Any], **params: Any) -> None: + def _run_one_experiment(self, algorithm: Any, total_args: dict[str, Any], **params: Any): """ Execute a single iteration of the experiment. @@ -332,14 +332,14 @@ def _run_one_experiment(self, algorithm: Any, total_args: dict[str, Any], **para **total_args, ) - def _create_and_save_run_data_frames(self, extra_data_frames: dict[str, pd.DataFrame] = None, final_save: bool = False) -> None: + def _create_and_save_run_data_frames(self, extra_data_frames: dict[str, pd.DataFrame] = None, final_save: bool = False): """ Save the collected run statistics and fitness curves to disk. Parameters ---------- extra_data_frames : dict[str, pd.DataFrame], optional - Additional data frames to save. + Additional DataFrames to save. final_save : bool, optional Whether this is the final save of the experiment (default False). """ @@ -357,7 +357,7 @@ def _create_and_save_run_data_frames(self, extra_data_frames: dict[str, pd.DataF for name, df in extra_data_frames.items(): self._dump_df_to_disk(df, df_name=name, final_save=final_save) - def _dump_df_to_disk(self, df: pd.DataFrame, df_name: str, final_save: bool = False) -> None: + def _dump_df_to_disk(self, df: pd.DataFrame, df_name: str, final_save: bool = False): """ Save the DataFrame to disk as both a pickle and CSV file. @@ -507,7 +507,7 @@ def _invoke_algorithm( return result - def start_run_timing(self) -> None: + def start_run_timing(self): """Start timing the experiment's execution.""" self._run_start_time = time.perf_counter() diff --git a/src/mlrose_ky/runners/ga_runner.py b/src/mlrose_ky/runners/ga_runner.py index 8d713b4b..5095f3bc 100644 --- a/src/mlrose_ky/runners/ga_runner.py +++ b/src/mlrose_ky/runners/ga_runner.py @@ -1,15 +1,7 @@ -"""Class for running optimization experiments using Genetic Algorithms (GA), including grid search functionality.""" - -# Authors: Andrew Rollings (modified by Kyle Nakamura) -# License: BSD 3-clause - -import mlrose_ky -from mlrose_ky.decorators import short_name -from mlrose_ky.runners._runner_base import _RunnerBase - """ -Example usage: +Class for running optimization experiments using Genetic Algorithms (GA), including grid search functionality. +Example usage: experiment_name = 'example_experiment' problem = TSPGenerator.generate(seed=SEED, number_of_cities=22) @@ -21,29 +13,83 @@ max_attempts=1000, population_sizes=[150, 200, 300], mutation_rates=[0.4, 0.5, 0.6]) - - # the two data frames will contain the results - df_run_stats, df_run_curves = ga.run() + + df_run_stats, df_run_curves = ga.run() """ +# Authors: Andrew Rollings (modified by Kyle Nakamura) +# License: BSD 3-clause + +from typing import Any + +import pandas as pd + +import mlrose_ky +from mlrose_ky.decorators import short_name +from mlrose_ky.runners._runner_base import _RunnerBase + @short_name("ga") class GARunner(_RunnerBase): + """ + A runner for performing optimization experiments using Genetic Algorithms (GA). + + This class extends _RunnerBase and provides functionality for running experiments + with Genetic Algorithms, including grid search over hyperparameters such as + population size and mutation rate. + + Attributes + ---------- + population_sizes : list[int] + List of population sizes to test in the grid search. + mutation_rates : list[float] + List of mutation rates to test in the grid search. + hamming_factors : Optional[list[float]] + Optional list of Hamming factors for the algorithm. + hamming_factor_decays : Optional[list[float]] + Optional list of Hamming factor decays. + """ def __init__( self, - problem, - experiment_name, - seed, - iteration_list, - population_sizes, - mutation_rates, - hamming_factors=None, - hamming_factor_decays=None, - max_attempts=500, - generate_curves=True, - **kwargs, + problem: Any, + experiment_name: str, + seed: int, + iteration_list: list[int], + population_sizes: list[int], + mutation_rates: list[float], + hamming_factors: list[float] = None, + hamming_factor_decays: list[float] = None, + max_attempts: int = 500, + generate_curves: bool = True, + **kwargs: Any, ): + """ + Initialize the GARunner class with problem data and various experiment parameters. + + Parameters + ---------- + problem : Any + The optimization problem to be solved. + experiment_name : str + Name of the experiment. + seed : int + Random seed for reproducibility. + iteration_list : list of int + List of iterations for the experiment. + population_sizes : list of int + List of population sizes to test in the grid search. + mutation_rates : list of float + List of mutation rates to test in the grid search. + hamming_factors : list of float, optional + Optional list of Hamming factors for the algorithm. + hamming_factor_decays : list of float, optional + Optional list of Hamming factor decays. + max_attempts : int, optional + Maximum number of attempts without improvement before stopping. + generate_curves : bool, optional + Whether to generate learning curves. + """ super().__init__( problem=problem, experiment_name=experiment_name, @@ -53,12 +99,24 @@ def __init__( generate_curves=generate_curves, **kwargs, ) - self.population_sizes = population_sizes - self.mutation_rates = mutation_rates - self.hamming_factors = hamming_factors - self.hamming_factor_decays = hamming_factor_decays + self.population_sizes: list[int] = population_sizes + self.mutation_rates: list[float] = mutation_rates + self.hamming_factors: list[float] = hamming_factors + self.hamming_factor_decays: list[float] = hamming_factor_decays + + def run(self) -> tuple[pd.DataFrame, pd.DataFrame]: + """ + Run the genetic algorithm experiment. + + This method performs grid search over the provided population sizes, + mutation rates, and optional Hamming factors and decays, and returns + the statistics and curves generated by the experiment. - def run(self): + Returns + ------- + tuple + A tuple containing two DataFrames: run statistics and run curves + """ return super().run_experiment( algorithm=mlrose_ky.genetic_alg, pop_size=("Population Size", self.population_sizes), diff --git a/src/mlrose_ky/runners/mimic_runner.py b/src/mlrose_ky/runners/mimic_runner.py index 6e9d3dca..513b873e 100644 --- a/src/mlrose_ky/runners/mimic_runner.py +++ b/src/mlrose_ky/runners/mimic_runner.py @@ -1,13 +1,6 @@ -"""Class for running optimization experiments using MIMIC, including grid search functionality.""" - -# Authors: Andrew Rollings (modified by Kyle Nakamura) -# License: BSD 3-clause - -import mlrose_ky -from mlrose_ky.decorators import short_name -from mlrose_ky.runners._runner_base import _RunnerBase - """ +Class for running optimization experiments using MIMIC, including grid search functionality. + Example usage: experiment_name = 'example_experiment' @@ -20,28 +13,78 @@ iteration_list=2 ** np.arange(10), max_attempts=500, keep_percent_list=[0.25, 0.5, 0.75]) - - # the two data frames will contain the results + df_run_stats, df_run_curves = mmc.run() """ +# Authors: Andrew Rollings (modified by Kyle Nakamura) +# License: BSD 3-clause + +from typing import Any + +import pandas as pd + +import mlrose_ky +from mlrose_ky.decorators import short_name +from mlrose_ky.runners._runner_base import _RunnerBase + @short_name("mimic") class MIMICRunner(_RunnerBase): + """ + A runner for performing optimization experiments using MIMIC. + + This class extends _RunnerBase and provides functionality for running experiments + with the MIMIC algorithm, including grid search over hyperparameters such as + population size and keep percent. + + Attributes + ---------- + keep_percent_list : list[float] + List of keep percentages to test in the grid search. + population_sizes : list[int] + List of population sizes to test in the grid search. + _use_fast_mimic : bool, optional + Whether to use the fast MIMIC mode, if available. + """ def __init__( self, - problem, - experiment_name, - seed, - iteration_list, - population_sizes, - keep_percent_list, - max_attempts=5, - generate_curves=True, - use_fast_mimic=True, - **kwargs, + problem: Any, + experiment_name: str, + seed: int, + iteration_list: list[int], + population_sizes: list[int], + keep_percent_list: list[float], + max_attempts: int = 5, + generate_curves: bool = True, + use_fast_mimic: bool = True, + **kwargs: Any, ): + """ + Initialize the MIMICRunner class with problem data and various experiment parameters. + + Parameters + ---------- + problem : Any + The optimization problem to be solved. + experiment_name : str + Name of the experiment. + seed : int + Random seed for reproducibility. + iteration_list : list of int + List of iterations for the experiment. + population_sizes : list of int + List of population sizes to test in the grid search. + keep_percent_list : list of float + List of keep percentages to test in the grid search. + max_attempts : int, optional + Maximum number of attempts without improvement before stopping. + generate_curves : bool, optional + Whether to generate learning curves. + use_fast_mimic : bool, optional + Whether to use the fast MIMIC mode, if available. + """ super().__init__( problem=problem, experiment_name=experiment_name, @@ -51,19 +94,38 @@ def __init__( generate_curves=generate_curves, **kwargs, ) - self.keep_percent_list = keep_percent_list - self.population_sizes = population_sizes - self._use_fast_mimic = None + self.keep_percent_list: list[float] = keep_percent_list + self.population_sizes: list[int] = population_sizes + self._use_fast_mimic: bool | None = None + + # Set fast MIMIC mode if available if hasattr(problem, "set_mimic_fast_mode") and callable(getattr(problem, "set_mimic_fast_mode")): self._use_fast_mimic = use_fast_mimic problem.set_mimic_fast_mode(use_fast_mimic) def _setup(self): + """ + Perform any necessary setup before running the experiment. + + Logs the current state of the fast MIMIC mode if it is enabled. + """ super()._setup() + if self._use_fast_mimic is not None: self.log_current_argument("use_fast_mimic", self._use_fast_mimic) - def run(self): + def run(self) -> tuple[pd.DataFrame | None, pd.DataFrame | None]: + """ + Run the MIMIC algorithm experiment. + + This method performs grid search over the provided population sizes + and keep percentages and returns the statistics and curves generated by the experiment. + + Returns + ------- + tuple + A tuple containing two DataFrames: run statistics and run curves + """ return super().run_experiment( algorithm=mlrose_ky.mimic, pop_size=("Population Size", self.population_sizes), diff --git a/src/mlrose_ky/runners/nngs_runner.py b/src/mlrose_ky/runners/nngs_runner.py index c7bdfb0f..752ab464 100644 --- a/src/mlrose_ky/runners/nngs_runner.py +++ b/src/mlrose_ky/runners/nngs_runner.py @@ -77,7 +77,7 @@ def __init__( generate_curves: bool = True, output_directory: str = None, **kwargs: Any, - ) -> None: + ): """ Initialize the NNGSRunner class with training and testing data and various experiment parameters. @@ -119,10 +119,6 @@ def __init__( Whether to generate learning curves. output_directory : str, optional Directory to save output. - - Returns - ------- - None """ # Take a copy of the grid search parameters grid_search_parameters = {**grid_search_parameters} diff --git a/src/mlrose_ky/runners/rhc_runner.py b/src/mlrose_ky/runners/rhc_runner.py index 62b0edc7..ad3ac06a 100644 --- a/src/mlrose_ky/runners/rhc_runner.py +++ b/src/mlrose_ky/runners/rhc_runner.py @@ -1,14 +1,6 @@ -"""Class for running optimization experiments using Random Hill Climbing (RHC), including grid search functionality.""" - -# Authors: Andrew Rollings (modified by Kyle Nakamura) -# License: BSD 3-clause - -import mlrose_ky -from mlrose_ky.decorators import short_name - -from mlrose_ky.runners._runner_base import _RunnerBase - """ +Class for running optimization experiments using Random Hill Climbing (RHC), including grid search functionality. + Example usage: experiment_name = 'example_experiment' @@ -20,17 +12,68 @@ seed=SEED, iteration_list=2 ** np.arange(10), max_attempts=5000, - restart_list=[25, 75, 100]) + restart_list=[25, 75, 100]) - # the two data frames will contain the results - df_run_stats, df_run_curves = rhc.run() + df_run_stats, df_run_curves = rhc.run() """ +# Authors: Andrew Rollings (modified by Kyle Nakamura) +# License: BSD 3-clause + +from typing import Any + +import pandas as pd + +import mlrose_ky +from mlrose_ky.decorators import short_name +from mlrose_ky.runners._runner_base import _RunnerBase + @short_name("rhc") class RHCRunner(_RunnerBase): + """ + A runner for performing optimization experiments using Random Hill Climbing (RHC). + + This class extends _RunnerBase and provides functionality for running experiments with the RHC algorithm, + including grid search over hyperparameters such as the number of restarts. + + Attributes + ---------- + restart_list : list[int] + List of restart values to test in the grid search. + """ - def __init__(self, problem, experiment_name, seed, iteration_list, restart_list, max_attempts=500, generate_curves=True, **kwargs): + def __init__( + self, + problem: Any, + experiment_name: str, + seed: int, + iteration_list: list[int], + restart_list: list[int], + max_attempts: int = 500, + generate_curves: bool = True, + **kwargs: Any, + ): + """ + Initialize the RHCRunner class with problem data and various experiment parameters. + + Parameters + ---------- + problem : Any + The optimization problem to be solved. + experiment_name : str + Name of the experiment. + seed : int + Random seed for reproducibility. + iteration_list : list of int + List of iterations for the experiment. + restart_list : list of int + List of restart values to test in the grid search. + max_attempts : int, optional + Maximum number of attempts without improvement before stopping. + generate_curves : bool, optional + Whether to generate learning curves. + """ super().__init__( problem=problem, experiment_name=experiment_name, @@ -40,7 +83,18 @@ def __init__(self, problem, experiment_name, seed, iteration_list, restart_list, generate_curves=generate_curves, **kwargs, ) - self.restart_list = restart_list + self.restart_list: list[int] = restart_list + + def run(self) -> tuple[pd.DataFrame | None, pd.DataFrame | None]: + """ + Run the Random Hill Climbing (RHC) experiment. + + This method performs grid search over the provided restart values and + returns the statistics and curves generated by the experiment. - def run(self): + Returns + ------- + tuple + A tuple containing two DataFrames: run statistics and run curves. + """ return super().run_experiment(algorithm=mlrose_ky.random_hill_climb, restarts=("Restarts", self.restart_list)) diff --git a/src/mlrose_ky/runners/sa_runner.py b/src/mlrose_ky/runners/sa_runner.py index bf298463..a0fdf2f7 100644 --- a/src/mlrose_ky/runners/sa_runner.py +++ b/src/mlrose_ky/runners/sa_runner.py @@ -1,20 +1,11 @@ -"""Class for running optimization experiments using Simulated Annealing (SA), including grid search functionality.""" - -# Authors: Andrew Rollings (modified by Kyle Nakamura) -# License: BSD 3-clause - -import numpy as np - -import mlrose_ky -from mlrose_ky.decorators import short_name -from mlrose_ky.runners._runner_base import _RunnerBase - """ +Class for running optimization experiments using Simulated Annealing (SA), including grid search functionality. + Example usage: experiment_name = 'example_experiment' problem = TSPGenerator.generate(seed=SEED, number_of_cities=22) - + # note that you can also initialize a temperature_list this way # temperature_list = [mlrose_ky.GeomDecay(init_temp=t, decay=d) for (t, d) in [(1, 0.99), (1e2, 0.999)]] # if you use this form, the decay_list parameter is ignored. @@ -28,26 +19,75 @@ temperature_list=[1, 10, 50, 100, 250, 500, 1000, 2500, 5000, 10000], decay_list=[mlrose_ky.GeomDecay]) - # the two data frames will contain the results - df_run_stats, df_run_curves = sa.run() + df_run_stats, df_run_curves = sa.run() """ +# Authors: Andrew Rollings (modified by Kyle Nakamura) +# License: BSD 3-clause + +from typing import Any, Callable + +import numpy as np +import pandas as pd + +import mlrose_ky +from mlrose_ky.decorators import short_name +from mlrose_ky.runners._runner_base import _RunnerBase + @short_name("sa") class SARunner(_RunnerBase): + """ + A runner for performing optimization experiments using Simulated Annealing (SA). + + This class extends _RunnerBase and provides functionality for running experiments + with the SA algorithm, including grid search over hyperparameters such as + temperature and decay schedules. + + Attributes + ---------- + temperature_list : list[float] + List of temperatures to test in the grid search. + decay_list : list[Callable], optional + List of decay schedules to use if raw temperature values are not provided. + use_raw_temp : bool + Whether to use raw temperature values or decay schedules. + """ def __init__( self, - problem, - experiment_name, - seed, - iteration_list, - temperature_list, - decay_list=None, - max_attempts=500, - generate_curves=True, - **kwargs, + problem: Any, + experiment_name: str, + seed: int, + iteration_list: list[int], + temperature_list: list[float], + decay_list: list[Callable] | None = None, + max_attempts: int = 500, + generate_curves: bool = True, + **kwargs: dict, ): + """ + Initialize the SARunner class with problem data and various experiment parameters. + + Parameters + ---------- + problem : Any + The optimization problem to be solved. + experiment_name : str + Name of the experiment. + seed : int + Random seed for reproducibility. + iteration_list : list of int + List of iterations for the experiment. + temperature_list : list of float + List of temperature values to test in the grid search. + decay_list : list of callable, optional + List of decay schedules to use, if raw temperature values are not provided. + max_attempts : int, optional + Maximum number of attempts without improvement before stopping. + generate_curves : bool, optional + Whether to generate learning curves. + """ super().__init__( problem=problem, experiment_name=experiment_name, @@ -58,14 +98,28 @@ def __init__( **kwargs, ) self.use_raw_temp = True - self.temperature_list = temperature_list + self.temperature_list: list[float] = temperature_list + + # Use decay schedules if provided if all([np.isscalar(x) for x in temperature_list]): if decay_list is None: decay_list = [mlrose_ky.GeomDecay] - self.decay_list = decay_list + + self.decay_list: list[Callable] = decay_list self.use_raw_temp = False - def run(self): + def run(self) -> tuple[pd.DataFrame | None, pd.DataFrame | None]: + """ + Run the Simulated Annealing (SA) experiment. + + This method performs grid search over the provided temperature values and + decay schedules, and returns the statistics and curves generated by the experiment. + + Returns + ------- + tuple + A tuple containing two DataFrames: run statistics and run curves. + """ temperatures = ( self.temperature_list if self.use_raw_temp else [d(init_temp=t) for t in self.temperature_list for d in self.decay_list] ) diff --git a/src/mlrose_ky/runners/skmlp_runner.py b/src/mlrose_ky/runners/skmlp_runner.py index d11b0216..6c6bc397 100644 --- a/src/mlrose_ky/runners/skmlp_runner.py +++ b/src/mlrose_ky/runners/skmlp_runner.py @@ -43,7 +43,7 @@ class _MLPClassifier(BaseEstimator): Additional keyword arguments to be passed to MLPClassifier. """ - def __init__(self, runner: "SKMLPRunner", **kwargs) -> None: + def __init__(self, runner: "SKMLPRunner", **kwargs): """ Initialize the _MLPClassifier wrapper. @@ -87,7 +87,7 @@ def __getattr__(self, item: str, default: Any = None) -> Any: return self.__dict__[item] if item in self.__dict__ else default - def __setattr__(self, item: str, value: Any) -> None: + def __setattr__(self, item: str, value: Any): """Set an attribute on the MLPClassifier if it exists, otherwise set it on the class instance.""" if "mlp" in self.__dict__ and hasattr(self.__dict__["mlp"], item): self.__dict__["mlp"].__setattr__(item, value) @@ -243,7 +243,7 @@ def _loss_grad_lbfgs_intercept( return f, g - def _invoke_runner_callback(self) -> None: + def _invoke_runner_callback(self): """Invoke the runner callback to save the current state of training.""" # noinspection PyProtectedMember no_improvement_count = self.mlp._no_improvement_count if hasattr(self.mlp, "_no_improvement_count") else 0 @@ -290,7 +290,7 @@ def __init__( output_directory: str = None, replay: bool = False, **kwargs: dict, - ) -> None: + ): """ Initialize the SKMLPRunner class with training and testing data and various experiment parameters. @@ -330,10 +330,6 @@ def __init__( Directory to save output. replay : bool, optional Whether to replay the experiment. - - Returns - ------- - None """ grid_search_parameters = {**grid_search_parameters} diff --git a/src/mlrose_ky/samples/synthetic_data.py b/src/mlrose_ky/samples/synthetic_data.py index efa677b1..009bacd1 100644 --- a/src/mlrose_ky/samples/synthetic_data.py +++ b/src/mlrose_ky/samples/synthetic_data.py @@ -205,7 +205,7 @@ def plot_synthetic_dataset( classifier: Any = None, transparent_bg: bool = False, bg_color: str = "white", -) -> None: +): """ Plot the synthetic dataset.