Skip to content

RandomOptimizer

RandomOptimizer #

Bases: Optimizer

A basic implementation of a random search optimizer.

Parameters:

  • cs (ConfigurationSpace) –

    Configuration space object.

  • max_fidelity (int) –

    Maximum fidelity level.

  • n (int) –

    Number of configurations to sample.

  • patience (int, default: None ) –

    Determines if early stopping should be applied for a single configuration. If the score does not improve for patience steps, the configuration is stopped. Defaults to None.

  • tol (float, default: 0.0 ) –

    Tolerance for early stopping. Training stops if the score does not improve by at least tol for patience iterations (if set). Values must be in the range [0.0, inf). Defaults to 0.0.

  • score_thresh (float, default: 0.0 ) –

    Score threshold for early stopping. Defaults to 0.0.

  • path (str, default: None ) –

    Path to save the optimizer. Defaults to None.

  • seed (int, default: None ) –

    Random seed. Defaults to None.

  • verbosity (int, default: 2 ) –

    Verbosity level. Defaults to 2.

Source code in src/qtt/optimizers/random.py
class RandomOptimizer(Optimizer):
    """A basic implementation of a random search optimizer.

    Args:
        cs (ConfigurationSpace): Configuration space object.
        max_fidelity (int): Maximum fidelity level.
        n (int): Number of configurations to sample.
        patience (int, optional): Determines if early stopping should be applied for a
            single configuration. If the score does not improve for `patience` steps,
            the configuration is stopped. Defaults to None.
        tol (float, optional): Tolerance for early stopping. Training stops if the score
            does not improve by at least `tol` for `patience` iterations (if set). Values
            must be in the range `[0.0, inf)`. Defaults to 0.0.
        score_thresh (float, optional): Score threshold for early stopping. Defaults to 0.0.
        path (str, optional): Path to save the optimizer. Defaults to None.
        seed (int, optional): Random seed. Defaults to None.
        verbosity (int, optional): Verbosity level. Defaults to 2.
    """

    def __init__(
        self,
        cs: ConfigurationSpace,
        max_fidelity: int,
        n: int,
        *,
        patience: int | None = None,
        tol: float = 0.0,
        score_thresh: float = 0.0,
        #
        path: str | None = None,
        seed: int | None = None,
        verbosity: int = 2,
    ):
        super().__init__(path=path)
        set_logger_verbosity(verbosity, logger)
        self.verbosity = verbosity

        if seed is not None:
            fix_random_seeds(seed)
        self.seed = seed

        self.cs = cs
        self.max_fidelity = max_fidelity
        self.candidates = cs.sample_configuration(n)
        self.N = n
        self.patience = patience
        self.tol = tol
        self.scr_thr = score_thresh

        self.reset()

    def reset(self):
        # trackers
        self.iteration = 0
        self.ask_count = 0
        self.tell_count = 0
        self.init_count = 0
        self.eval_count = 0
        self.evaled = set()
        self.stoped = set()
        self.failed = set()
        self.history = []

        self.fidelities: np.ndarray = np.zeros(self.N, dtype=int)
        self.curves: np.ndarray = np.zeros((self.N, self.max_fidelity), dtype=float)
        self.costs: np.ndarray = np.zeros(self.N, dtype=float)

        if self.patience is not None:
            self._score_history = np.zeros((self.N, self.patience), dtype=float)

    def ask(self):
        left = set(range(self.N)) - self.failed - self.stoped
        index = random.choice(list(left))

        fidelity = self.fidelities[index] + 1

        return {
            "config_id": index,
            "config": self.candidates[index],
            "fidelity": fidelity,
        }

    def tell(self, reports: dict | list):
        if isinstance(reports, dict):
            reports = [reports]
        for report in reports:
            self._tell(report)

    def _tell(self, report: dict):
        self.tell_count += 1

        index = report["config_id"]
        fidelity = report["fidelity"]
        cost = report["cost"]
        score = report["score"]
        status = report["status"]

        if not status:
            self.failed.add(index)
            return

        # update trackers
        self.curves[index, fidelity - 1] = score
        self.fidelities[index] = fidelity
        self.costs[index] = cost
        self.history.append(report)
        self.evaled.add(index)
        self.eval_count += 1

        if score >= 1.0 - self.scr_thr or fidelity == self.max_fidelity:
            self.stoped.add(index)

        if self.patience is not None:
            if not np.any(self._score_history[index] < (score - self.tol)):
                self.stoped.add(index)
            self._score_history[index][fidelity % self.patience] = score

ante() #

This method is intended for the use with a tuner. It allows to perform some pre-processing steps before each ask.

Source code in src/qtt/optimizers/optimizer.py
def ante(self):
    """This method is intended for the use with a tuner.
    It allows to perform some pre-processing steps before each ask."""
    pass

load(path, reset_paths=True, verbose=True) classmethod #

Loads the model from disk to memory.

Parameters:

  • path (str) –

    Path to the saved model, minus the file name. This should generally be a directory path ending with a '/' character (or appropriate path separator value depending on OS). The model file is typically located in os.path.join(path, cls.model_file_name).

  • reset_paths (bool, default: True ) –

    Whether to reset the self.path value of the loaded model to be equal to path. It is highly recommended to keep this value as True unless accessing the original self.path value is important. If False, the actual valid path and self.path may differ, leading to strange behaviour and potential exceptions if the model needs to load any other files at a later time.

  • verbose (bool, default: True ) –

    Whether to log the location of the loaded file.

Returns:

  • model ( Optimizer ) –

    The loaded model object.

Source code in src/qtt/optimizers/optimizer.py
@classmethod
def load(cls, path: str, reset_paths: bool = True, verbose: bool = True):
    """
    Loads the model from disk to memory.

    Args:
        path (str):
            Path to the saved model, minus the file name.
            This should generally be a directory path ending with a '/' character (or appropriate path separator value depending on OS).
            The model file is typically located in os.path.join(path, cls.model_file_name).
        reset_paths (bool):
            Whether to reset the self.path value of the loaded model to be equal to path.
            It is highly recommended to keep this value as True unless accessing the original self.path value is important.
            If False, the actual valid path and self.path may differ, leading to strange behaviour and potential exceptions if the model needs to load any other files at a later time.
        verbose (bool):
            Whether to log the location of the loaded file.

    Returns:
        model (Optimizer): The loaded model object.
    """
    file_path = os.path.join(path, cls.model_file_name)
    with open(file_path, "rb") as f:
        model = pickle.load(f)
    if reset_paths:
        model.path = path
    if verbose:
        logger.info(f"Model loaded from: {file_path}")
    return model

post() #

This method is intended for the use with a tuner. It allows to perform some post-processing steps after each tell.

Source code in src/qtt/optimizers/optimizer.py
def post(self):
    """This method is intended for the use with a tuner.
    It allows to perform some post-processing steps after each tell."""
    pass

reset_path(path=None) #

Reset the path of the model.

Parameters:

  • path (str, default: None ) –

    Directory location to store all outputs. If None, a new unique time-stamped directory is chosen.

Source code in src/qtt/optimizers/optimizer.py
def reset_path(self, path: str | None = None):
    """Reset the path of the model.

    Args:
        path (str):
            Directory location to store all outputs.
            If None, a new unique time-stamped directory is chosen.
    """
    if path is None:
        path = setup_outputdir(path=self.name.lower(), path_suffix=self.name)
    self.path = path

save(path=None, verbose=True) #

Saves the model to disk.

Parameters:

  • path (str, default: None ) –

    Path to the saved model, minus the file name. This should generally be a directory path ending with a '/' character (or appropriate path separator value depending on OS). If None, self.path is used. The final model file is typically saved to os.path.join(path, self.model_file_name).

  • verbose (bool, default: True ) –

    Whether to log the location of the saved file.

Returns:

  • str ( str ) –

    Path to the saved model, minus the file name. Use this value to load the model from disk via cls.load(path), where cls is the class of the model object (e.g., model = Model.load(path)).

Source code in src/qtt/optimizers/optimizer.py
def save(self, path: str | None = None, verbose: bool = True) -> str:
    """
    Saves the model to disk.

    Args:
        path (str): Path to the saved model, minus the file name. This should generally
            be a directory path ending with a '/' character (or appropriate path separator
            value depending on OS). If None, self.path is used. The final model file is
            typically saved to os.path.join(path, self.model_file_name).
        verbose (bool): Whether to log the location of the saved file.

    Returns:
        str: Path to the saved model, minus the file name. Use this value to load the
            model from disk via cls.load(path), where cls is the class of the model
            object (e.g., model = Model.load(path)).
    """
    if path is None:
        path = self.path
    os.makedirs(path, exist_ok=True)
    file_path = os.path.join(path, self.model_file_name)
    # tmp = {}
    # for key, obj in vars(self).items():
    #     if hasattr(obj, "save"):
    #         obj_path = os.path.join(path, key)
    #         obj.save(obj_path)
    #         tmp[key] = obj
    #         setattr(self, key, None)
    with open(file_path, "wb") as f:
        pickle.dump(self, f, protocol=pickle.HIGHEST_PROTOCOL)
    # for key, obj in tmp.items():
    #     setattr(self, key, obj)
    if verbose:
        logger.info(f"Model saved to: {file_path}")
    return path