diff --git a/drevalpy/experiment.py b/drevalpy/experiment.py index 635c1ea3..e2d95f2b 100644 --- a/drevalpy/experiment.py +++ b/drevalpy/experiment.py @@ -49,6 +49,7 @@ def drug_response_experiment( path_data: str = "data", model_checkpoint_dir: str = "TEMPORARY", hyperparameter_tuning=True, + n_trials: int = 20, final_model_on_full_data: bool = False, wandb_project: str | None = None, ) -> None: @@ -99,7 +100,9 @@ def drug_response_experiment( :param overwrite: whether to overwrite existing results :param path_data: path to the data directory, usually data/ :param model_checkpoint_dir: directory to save model checkpoints. If "TEMPORARY", a temporary directory is created. - :param hyperparameter_tuning: whether to run in debug mode - if False, only select first hyperparameter set + :param hyperparameter_tuning: whether to perform hyperparameter tuning. If False, uses the first hyperparameter + configuration from the search space. + :param n_trials: number of Bayesian optimization trials for hyperparameter tuning. Default is 20. :param final_model_on_full_data: if True, a final/production model is saved in the results directory. If hyperparameter_tuning is true, the final model is produced according to the hyperparameter tuning procedure which was evaluated in the nested cross validation. @@ -173,8 +176,12 @@ def drug_response_experiment( ) parent_dir = os.path.dirname(predictions_path) - model_hpam_set = model_class.get_hyperparameter_set() - if not hyperparameter_tuning: + if hyperparameter_tuning: + # Use raw search space for Bayesian optimization + model_hpam_set = model_class.get_hyperparameter_search_space() + else: + # Use expanded grid and take first (default) configuration + model_hpam_set = model_class.get_hyperparameter_set() model_hpam_set = [model_hpam_set[0]] if response_data.cv_splits is None: @@ -222,6 +229,7 @@ def drug_response_experiment( "metric": hpam_optimization_metric, "path_data": path_data, "model_checkpoint_dir": model_checkpoint_dir, + "n_trials": n_trials, } # During hyperparameter tuning, create separate wandb runs per trial if enabled @@ -383,6 +391,7 @@ def drug_response_experiment( test_mode=test_mode, val_ratio=0.1, hyperparameter_tuning=hyperparameter_tuning, + n_trials=n_trials, ) consolidate_single_drug_model_predictions( @@ -1148,53 +1157,173 @@ def train_and_evaluate( return results +def _deep_equal(a: Any, b: Any) -> bool: + """ + Compare two values for equality, handling nested structures. + + :param a: first value + :param b: second value + :returns: True if values are equal (including nested structures) + """ + if isinstance(a, list) and isinstance(b, list): + if len(a) != len(b): + return False + return all(_deep_equal(ai, bi) for ai, bi in zip(a, b, strict=True)) + elif isinstance(a, dict) and isinstance(b, dict): + if set(a.keys()) != set(b.keys()): + return False + return all(_deep_equal(a[k], b[k]) for k in a.keys()) + else: + return a == b + + +def _sample_hyperparameters_from_search_space(trial, search_space: dict[str, Any]) -> dict[str, Any]: + """ + Sample hyperparameters from a search space definition using Optuna. + + :param trial: Optuna trial object + :param search_space: dictionary mapping parameter names to their search space definitions + :returns: dictionary of sampled hyperparameters + :raises ValueError: if an unknown parameter type is encountered in the search space + """ + sampled = {} + for param_name, param_def in search_space.items(): + if isinstance(param_def, dict) and "type" in param_def: + # Structured search space definition for continuous ranges + if "default" not in param_def: + raise ValueError( + f"Hyperparameter '{param_name}' has continuous range definition " + f"but missing required 'default' field. " + f"Please add a 'default' value to use when hyperparameter_tuning=False." + ) + param_type = param_def["type"] + low = param_def["low"] + high = param_def["high"] + log_scale = param_def.get("log", False) + + if param_type == "int": + sampled[param_name] = trial.suggest_int(param_name, low, high, log=log_scale) + elif param_type == "float": + if log_scale: + sampled[param_name] = trial.suggest_float(param_name, low, high, log=True) + else: + sampled[param_name] = trial.suggest_float(param_name, low, high) + else: + raise ValueError(f"Unknown parameter type: {param_type}") + elif isinstance(param_def, list): + # Categorical choices + if len(param_def) == 1: + # Single value, no tuning needed + sampled[param_name] = param_def[0] + else: + sampled[param_name] = trial.suggest_categorical(param_name, param_def) + else: + # Single fixed value (not a list or dict) + sampled[param_name] = param_def + + return sampled + + def hpam_tune( model: DRPModel, train_dataset: DrugResponseDataset, validation_dataset: DrugResponseDataset, - hpam_set: list[dict], + hpam_set: list[dict] | dict[str, Any], early_stopping_dataset: DrugResponseDataset | None = None, response_transformation: TransformerMixin | None = None, metric: str = "RMSE", path_data: str = "data", model_checkpoint_dir: str = "TEMPORARY", + n_trials: int = 20, *, split_index: int | None = None, wandb_project: str | None = None, wandb_base_config: dict[str, Any] | None = None, ) -> dict: """ - Tune the hyperparameters for the given model in an iterative manner. + Tune hyperparameters using Bayesian optimization with Optuna. + + This function uses Optuna's TPE (Tree-structured Parzen Estimator) sampler + for efficient hyperparameter search. Trials are run sequentially. :param model: model to use :param train_dataset: training dataset :param validation_dataset: validation dataset - :param hpam_set: hyperparameters to tune + :param hpam_set: either a search space dictionary (for Bayesian optimization) or + a list of hyperparameter configurations (legacy grid search format) :param early_stopping_dataset: early stopping dataset :param response_transformation: normalizer to use for the response data :param metric: metric to evaluate which model is the best :param path_data: path to the data directory, e.g., data/ :param model_checkpoint_dir: directory to save model checkpoints + :param n_trials: number of Bayesian optimization trials to run :param split_index: optional CV split index, used for naming wandb runs :param wandb_project: optional wandb project name; if provided, enables per-trial wandb runs :param wandb_base_config: optional base config dict to include in each wandb run :returns: best hyperparameters :raises AssertionError: if hpam_set is empty """ - if len(hpam_set) == 0: - raise AssertionError("hpam_set must contain at least one hyperparameter configuration") - if len(hpam_set) == 1: - return hpam_set[0] + import optuna + from optuna.samplers import TPESampler + + # Handle legacy list format (grid search) - convert to search space + if isinstance(hpam_set, list): + if len(hpam_set) == 0: + raise AssertionError("hpam_set must contain at least one hyperparameter configuration") + if len(hpam_set) == 1: + return hpam_set[0] + + # Convert list of dicts to search space by extracting unique values per parameter + # Handle nested structures (like lists of lists) by using a list-based approach + search_space: dict[str, Any] = {} + all_keys: set[str] = set() + for config in hpam_set: + all_keys.update(config.keys()) + + for key in all_keys: + # Collect all values for this key, preserving order and handling unhashable types + values: list[Any] = [] + seen: list[Any] = [] + for config in hpam_set: + if key in config: + value = config[key] # Use direct access since we know key exists + # For unhashable types (lists, dicts), use deep comparison + if isinstance(value, (list, dict)): + # Check if we've seen an equivalent value + if not any(_deep_equal(value, v) for v in seen): + values.append(value) + seen.append(value) + else: + # For hashable types, use set for deduplication + if value not in values: + values.append(value) + if len(values) == 1: + search_space[key] = values[0] + else: + search_space[key] = values + else: + search_space = hpam_set + + # Check if there's anything to tune + tunable_params = [ + k for k, v in search_space.items() if isinstance(v, (list, dict)) and (not isinstance(v, list) or len(v) > 1) + ] + if not tunable_params: + # No tuning needed, return fixed values + return {k: (v[0] if isinstance(v, list) else v) for k, v in search_space.items()} # Mark that we're in hyperparameter tuning phase - # This prevents updating wandb.config during tuning - we'll only log final best hyperparameters model._in_hyperparameter_tuning = True - best_hyperparameters = None mode = get_mode(metric) - best_score = float("inf") if mode == "min" else float("-inf") - for trial_idx, hyperparameter in enumerate(hpam_set): - print(f"Training model with hyperparameters: {hyperparameter}") + direction = "minimize" if mode == "min" else "maximize" + + def objective(trial): + # Sample hyperparameters + hyperparameter = _sample_hyperparameters_from_search_space(trial, search_space) + trial_idx = trial.number + + print(f"Trial {trial_idx}: Training model with hyperparameters: {hyperparameter}") # Create a separate wandb run for each hyperparameter trial if enabled if wandb_project is not None: @@ -1222,40 +1351,52 @@ def hpam_tune( finish_previous=True, ) - # During hyperparameter tuning, don't update wandb config via log_hyperparameters - # Trial hyperparameters are stored in wandb.config for each run - score = train_and_evaluate( - model=model, - hpams=hyperparameter, - path_data=path_data, - train_dataset=train_dataset, - validation_dataset=validation_dataset, - early_stopping_dataset=early_stopping_dataset, - metric=metric, - response_transformation=response_transformation, - model_checkpoint_dir=model_checkpoint_dir, - )[metric] + try: + score = train_and_evaluate( + model=model, + hpams=hyperparameter, + path_data=path_data, + train_dataset=train_dataset, + validation_dataset=validation_dataset, + early_stopping_dataset=early_stopping_dataset, + metric=metric, + response_transformation=response_transformation, + model_checkpoint_dir=model_checkpoint_dir, + )[metric] + + if np.isnan(score): + # Return a bad score for NaN results + score = float("inf") if mode == "min" else float("-inf") + else: + print(f"Trial {trial_idx}: {metric} = {np.round(score, 4)}") - # Note: train_and_evaluate() already logs val_* metrics once via - # DRPModel.compute_and_log_final_metrics(..., prefix="val_"). - # Avoid logging val_{metric} again here (it would create duplicate points). - if np.isnan(score): + except Exception as e: + print(f"Trial {trial_idx} failed: {e}") + score = float("inf") if mode == "min" else float("-inf") + + finally: if model.is_wandb_enabled(): model.finish_wandb() - continue - if (mode == "min" and score < best_score) or (mode == "max" and score > best_score): - print(f"current best {metric} score: {np.round(score, 3)}") - best_score = score - best_hyperparameters = hyperparameter + return score + + # Create and run the Optuna study + study = optuna.create_study(direction=direction, sampler=TPESampler(seed=42)) + study.optimize(objective, n_trials=n_trials, show_progress_bar=True) + + # Get best hyperparameters + best_hyperparameters = study.best_params - # Close this trial's run after all logging is done - if model.is_wandb_enabled(): - model.finish_wandb() + # Fill in fixed parameters that weren't tuned + for key, value in search_space.items(): + if key not in best_hyperparameters: + if isinstance(value, list) and len(value) == 1: + best_hyperparameters[key] = value[0] + elif not isinstance(value, (list, dict)): + best_hyperparameters[key] = value - if best_hyperparameters is None: - warnings.warn("all hpams lead to NaN respone. using last hpam combination.", stacklevel=2) - best_hyperparameters = hyperparameter + print(f"\nBest trial: {study.best_trial.number}") + print(f"Best {metric}: {np.round(study.best_value, 4)}") return best_hyperparameters @@ -1265,50 +1406,137 @@ def hpam_tune_raytune( train_dataset: DrugResponseDataset, validation_dataset: DrugResponseDataset, early_stopping_dataset: DrugResponseDataset | None, - hpam_set: list[dict], + hpam_set: list[dict] | dict[str, Any], response_transformation: TransformerMixin | None = None, metric: str = "RMSE", ray_path: str = "raytune", path_data: str = "data", model_checkpoint_dir: str = "TEMPORARY", + n_trials: int = 20, ) -> dict: """ - Tune the hyperparameters for the given model using Ray Tune. Ray[tune] must be installed. + Tune hyperparameters using Bayesian optimization with Ray Tune and Optuna. + + This function uses Ray Tune with OptunaSearch for parallel Bayesian optimization. + Ray[tune] and optuna must be installed. :param model: model to use :param train_dataset: training dataset :param validation_dataset: validation dataset :param early_stopping_dataset: early stopping dataset - :param hpam_set: hyperparameters to tune + :param hpam_set: either a search space dictionary (for Bayesian optimization) or + a list of hyperparameter configurations (legacy grid search format) :param response_transformation: normalizer for response data :param metric: evaluation metric :param ray_path: path to the raytune directory :param path_data: path to data directory, e.g., data/ :param model_checkpoint_dir: directory for model checkpoints + :param n_trials: number of Bayesian optimization trials to run :returns: best hyperparameters + :raises AssertionError: if hpam_set is empty :raises ValueError: if best_result is None """ - print("Starting hyperparameter tuning with Ray Tune ...") - print(f"Hyperparameter combinations to evaluate: {len(hpam_set)}") - print() - - if len(hpam_set) == 1: - return hpam_set[0] - import ray from ray import tune + from ray.tune.search.optuna import OptunaSearch + + print("Starting hyperparameter tuning with Ray Tune (Bayesian optimization) ...") + + # Handle legacy list format (grid search) - convert to search space + if isinstance(hpam_set, list): + if len(hpam_set) == 0: + raise AssertionError("hpam_set must contain at least one hyperparameter configuration") + if len(hpam_set) == 1: + return hpam_set[0] + + # Convert list of dicts to search space + search_space: dict[str, Any] = {} + all_keys: set[str] = set() + for config in hpam_set: + all_keys.update(config.keys()) + + for key in all_keys: + # Collect all values for this key, preserving order and handling unhashable types + values: list[Any] = [] + seen: list[Any] = [] + for config in hpam_set: + if key in config: + value = config[key] # Use direct access since we know key exists + # For unhashable types (lists, dicts), use deep comparison + if isinstance(value, (list, dict)): + # Check if we've seen an equivalent value + if not any(_deep_equal(value, v) for v in seen): + values.append(value) + seen.append(value) + else: + # For hashable types, use set for deduplication + if value not in values: + values.append(value) + if len(values) == 1: + search_space[key] = values[0] + else: + search_space[key] = values + else: + search_space = hpam_set + + # Check if there's anything to tune + tunable_params = [ + k for k, v in search_space.items() if isinstance(v, (list, dict)) and (not isinstance(v, list) or len(v) > 1) + ] + if not tunable_params: + return {k: (v[0] if isinstance(v, list) else v) for k, v in search_space.items()} + + # Convert search space to Ray Tune format + ray_search_space = {} + fixed_params = {} + for param_name, param_def in search_space.items(): + if isinstance(param_def, dict) and "type" in param_def: + if "default" not in param_def: + raise ValueError( + f"Hyperparameter '{param_name}' has continuous range definition " + f"but missing required 'default' field. " + f"Please add a 'default' value to use when hyperparameter_tuning=False." + ) + param_type = param_def["type"] + low = param_def["low"] + high = param_def["high"] + log_scale = param_def.get("log", False) + + if param_type == "int": + if log_scale: + ray_search_space[param_name] = tune.lograndint(low, high) + else: + ray_search_space[param_name] = tune.randint(low, high + 1) + elif param_type == "float": + if log_scale: + ray_search_space[param_name] = tune.loguniform(low, high) + else: + ray_search_space[param_name] = tune.uniform(low, high) + elif isinstance(param_def, list): + if len(param_def) == 1: + fixed_params[param_name] = param_def[0] + else: + ray_search_space[param_name] = tune.choice(param_def) + else: + fixed_params[param_name] = param_def + + print(f"Tunable parameters: {list(ray_search_space.keys())}") + print(f"Fixed parameters: {list(fixed_params.keys())}") + print(f"Number of trials: {n_trials}") + print() path_data = os.path.abspath(path_data) if not ray.is_initialized(): ray.init(_temp_dir=os.path.join(os.path.expanduser("~"), "raytmp")) resources_per_trial = {"gpu": 1} if torch.cuda.is_available() else {"cpu": 1} - def trainable(hpams): + def trainable(config): try: - inner = hpams["hpams"] + # Merge sampled params with fixed params + hyperparameter = {**fixed_params, **config} result = train_and_evaluate( model=model, - hpams=inner, + hpams=hyperparameter, path_data=path_data, train_dataset=train_dataset, validation_dataset=validation_dataset, @@ -1317,35 +1545,50 @@ def trainable(hpams): response_transformation=response_transformation, model_checkpoint_dir=model_checkpoint_dir, ) - tune.report(metrics={metric: result[metric]}) + return {metric: result[metric]} except Exception as e: import traceback print("Trial failed:", e) traceback.print_exc() + # Return bad score on failure + mode = get_mode(metric) + return {metric: float("inf") if mode == "min" else float("-inf")} trainable = tune.with_resources(trainable, resources_per_trial) - param_space = {"hpams": tune.grid_search(hpam_set)} + + mode = get_mode(metric) + optuna_search = OptunaSearch(metric=metric, mode=mode, seed=42) tuner = tune.Tuner( trainable, - param_space=param_space, + param_space=ray_search_space, run_config=tune.RunConfig( storage_path=ray_path, name="hpam_tuning", ), tune_config=tune.TuneConfig( metric=metric, - mode=get_mode(metric), + mode=mode, + search_alg=optuna_search, + num_samples=n_trials, + max_concurrent_trials=1, # Run one at a time for Bayesian optimization ), ) results = tuner.fit() - best_result = results.get_best_result(metric=metric, mode=get_mode(metric)) + best_result = results.get_best_result(metric=metric, mode=mode) ray.shutdown() + if best_result.config is None: raise ValueError("Ray failed; no best result.") - return best_result.config["hpams"] + + # Merge best config with fixed params + best_hyperparameters = {**fixed_params, **best_result.config} + + print(f"\nBest {metric}: {np.round(best_result.metrics[metric], 4)}") + + return best_hyperparameters @pipeline_function @@ -1470,6 +1713,7 @@ def train_final_model( test_mode: str = "LCO", val_ratio: float = 0.1, hyperparameter_tuning: bool = True, + n_trials: int = 20, ) -> None: """ Final Production Model Training. @@ -1493,6 +1737,7 @@ def train_final_model( :param test_mode: split logic for validation (LCO, LDO, LTO, LPO) :param val_ratio: validation size ratio :param hyperparameter_tuning: whether to perform hyperparameter tuning + :param n_trials: number of Bayesian optimization trials for hyperparameter tuning """ print("Training final model with application-specific validation strategy ...") @@ -1512,8 +1757,9 @@ def train_final_model( else: early_stopping_dataset = None - hpam_set = model.get_hyperparameter_set() if hyperparameter_tuning: + # Use raw search space for Bayesian optimization + hpam_set = model.get_hyperparameter_search_space() best_hpams = hpam_tune( model=model, train_dataset=train_dataset, @@ -1524,8 +1770,11 @@ def train_final_model( metric=metric, path_data=path_data, model_checkpoint_dir=model_checkpoint_dir, + n_trials=n_trials, ) else: + # Use expanded grid and take first (default) configuration + hpam_set = model.get_hyperparameter_set() best_hpams = hpam_set[0] print(f"Best hyperparameters for final model: {best_hpams}") diff --git a/drevalpy/models/drp_model.py b/drevalpy/models/drp_model.py index 7599be2e..cde01b67 100644 --- a/drevalpy/models/drp_model.py +++ b/drevalpy/models/drp_model.py @@ -282,13 +282,81 @@ def get_hyperparameter_set(cls) -> list[dict[str, Any]]: if hpams is None: return [{}] - # each param should be a list + # Convert continuous ranges to default values for grid expansion + # This handles the case when hyperparameter_tuning=False + processed_hpams: dict[str, Any] = {} for hp in hpams: - if not isinstance(hpams[hp], list): - hpams[hp] = [hpams[hp]] - grid = list(ParameterGrid(hpams)) + value = hpams[hp] + # If it's a continuous range definition, require and use the default value + if isinstance(value, dict) and "type" in value: + if "default" not in value: + raise ValueError( + f"Hyperparameter '{hp}' has continuous range definition but missing required 'default' field. " + f"Please add a 'default' value to use when hyperparameter_tuning=False." + ) + # Validate default is within range + low = value["low"] + high = value["high"] + default = value["default"] + param_type = value["type"] + + if param_type == "int": + if not isinstance(default, int): + raise ValueError( + f"Hyperparameter '{hp}': default must be an integer, got {type(default).__name__}" + ) + if default < low or default > high: + raise ValueError( + f"Hyperparameter '{hp}': default value {default} is outside range [{low}, {high}]" + ) + elif param_type == "float": + if not isinstance(default, (int, float)): + raise ValueError( + f"Hyperparameter '{hp}': default must be a float, got {type(default).__name__}" + ) + if default < low or default > high: + raise ValueError( + f"Hyperparameter '{hp}': default value {default} is outside range [{low}, {high}]" + ) + + processed_hpams[hp] = [default] + elif isinstance(value, list): + processed_hpams[hp] = value + else: + # Single value + processed_hpams[hp] = [value] + grid = list(ParameterGrid(processed_hpams)) return grid + @classmethod + @pipeline_function + def get_hyperparameter_search_space(cls) -> dict[str, Any]: + """ + Load the raw hyperparameter search space from a YAML file. + + This method returns the search space definition without expanding it into + all combinations. Useful for Bayesian optimization where we sample from + the space rather than enumerating all combinations. + + :returns: dictionary mapping parameter names to their search space definitions + :raises ValueError: if the hyperparameters are not in the correct format + :raises KeyError: if the model is not found in the hyperparameters file + """ + hyperparameter_file = os.path.join(os.path.dirname(inspect.getfile(cls)), "hyperparameters.yaml") + + with open(hyperparameter_file, encoding="utf-8") as f: + try: + hpams = yaml.safe_load(f)[cls.get_model_name()] + except yaml.YAMLError as exc: + raise ValueError(f"Error in hyperparameters.yaml: {exc}") from exc + except KeyError as key_exc: + raise KeyError(f"Model {cls.get_model_name()} not found in hyperparameters.yaml") from key_exc + + if hpams is None: + return {} + + return hpams + @property @abstractmethod def cell_line_views(self) -> list[str]: diff --git a/poetry.lock b/poetry.lock index 6e8e7a5e..ec9d6845 100644 --- a/poetry.lock +++ b/poetry.lock @@ -182,6 +182,26 @@ files = [ {file = "alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e"}, ] +[[package]] +name = "alembic" +version = "1.18.1" +description = "A database migration tool for SQLAlchemy." +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "alembic-1.18.1-py3-none-any.whl", hash = "sha256:f1c3b0920b87134e851c25f1f7f236d8a332c34b75416802d06971df5d1b7810"}, + {file = "alembic-1.18.1.tar.gz", hash = "sha256:83ac6b81359596816fb3b893099841a0862f2117b2963258e965d70dc62fb866"}, +] + +[package.dependencies] +Mako = "*" +SQLAlchemy = ">=1.4.0" +typing-extensions = ">=4.12" + +[package.extras] +tz = ["tzdata"] + [[package]] name = "annotated-types" version = "0.7.0" @@ -710,7 +730,7 @@ version = "6.10.1" description = "Add colours to the output of Python's logging module." optional = false python-versions = ">=3.6" -groups = ["development"] +groups = ["main", "development"] files = [ {file = "colorlog-6.10.1-py3-none-any.whl", hash = "sha256:2d7e8348291948af66122cff006c9f8da6255d224e7cf8e37d8de2df3bad8c9c"}, {file = "colorlog-6.10.1.tar.gz", hash = "sha256:eb4ae5cb65fe7fec7773c2306061a8e63e02efc2c72eba9d27b0fa23c94f1321"}, @@ -1533,6 +1553,69 @@ gitdb = ">=4.0.1,<5" doc = ["sphinx (>=7.1.2,<7.2)", "sphinx-autodoc-typehints", "sphinx_rtd_theme"] test = ["coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock ; python_version < \"3.8\"", "mypy (==1.18.2) ; python_version >= \"3.9\"", "pre-commit", "pytest (>=7.3.1)", "pytest-cov", "pytest-instafail", "pytest-mock", "pytest-sugar", "typing-extensions ; python_version < \"3.11\""] +[[package]] +name = "greenlet" +version = "3.3.0" +description = "Lightweight in-process concurrent programming" +optional = false +python-versions = ">=3.10" +groups = ["main"] +markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\"" +files = [ + {file = "greenlet-3.3.0-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:6f8496d434d5cb2dce025773ba5597f71f5410ae499d5dd9533e0653258cdb3d"}, + {file = "greenlet-3.3.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b96dc7eef78fd404e022e165ec55327f935b9b52ff355b067eb4a0267fc1cffb"}, + {file = "greenlet-3.3.0-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:73631cd5cccbcfe63e3f9492aaa664d278fda0ce5c3d43aeda8e77317e38efbd"}, + {file = "greenlet-3.3.0-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b299a0cb979f5d7197442dccc3aee67fce53500cd88951b7e6c35575701c980b"}, + {file = "greenlet-3.3.0-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7dee147740789a4632cace364816046e43310b59ff8fb79833ab043aefa72fd5"}, + {file = "greenlet-3.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:39b28e339fc3c348427560494e28d8a6f3561c8d2bcf7d706e1c624ed8d822b9"}, + {file = "greenlet-3.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b3c374782c2935cc63b2a27ba8708471de4ad1abaa862ffdb1ef45a643ddbb7d"}, + {file = "greenlet-3.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:b49e7ed51876b459bd645d83db257f0180e345d3f768a35a85437a24d5a49082"}, + {file = "greenlet-3.3.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:e29f3018580e8412d6aaf5641bb7745d38c85228dacf51a73bd4e26ddf2a6a8e"}, + {file = "greenlet-3.3.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a687205fb22794e838f947e2194c0566d3812966b41c78709554aa883183fb62"}, + {file = "greenlet-3.3.0-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4243050a88ba61842186cb9e63c7dfa677ec146160b0efd73b855a3d9c7fcf32"}, + {file = "greenlet-3.3.0-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:670d0f94cd302d81796e37299bcd04b95d62403883b24225c6b5271466612f45"}, + {file = "greenlet-3.3.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6cb3a8ec3db4a3b0eb8a3c25436c2d49e3505821802074969db017b87bc6a948"}, + {file = "greenlet-3.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2de5a0b09eab81fc6a382791b995b1ccf2b172a9fec934747a7a23d2ff291794"}, + {file = "greenlet-3.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4449a736606bd30f27f8e1ff4678ee193bc47f6ca810d705981cfffd6ce0d8c5"}, + {file = "greenlet-3.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:7652ee180d16d447a683c04e4c5f6441bae7ba7b17ffd9f6b3aff4605e9e6f71"}, + {file = "greenlet-3.3.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:b01548f6e0b9e9784a2c99c5651e5dc89ffcbe870bc5fb2e5ef864e9cc6b5dcb"}, + {file = "greenlet-3.3.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:349345b770dc88f81506c6861d22a6ccd422207829d2c854ae2af8025af303e3"}, + {file = "greenlet-3.3.0-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e8e18ed6995e9e2c0b4ed264d2cf89260ab3ac7e13555b8032b25a74c6d18655"}, + {file = "greenlet-3.3.0-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c024b1e5696626890038e34f76140ed1daf858e37496d33f2af57f06189e70d7"}, + {file = "greenlet-3.3.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:047ab3df20ede6a57c35c14bf5200fcf04039d50f908270d3f9a7a82064f543b"}, + {file = "greenlet-3.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2d9ad37fc657b1102ec880e637cccf20191581f75c64087a549e66c57e1ceb53"}, + {file = "greenlet-3.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:83cd0e36932e0e7f36a64b732a6f60c2fc2df28c351bae79fbaf4f8092fe7614"}, + {file = "greenlet-3.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:a7a34b13d43a6b78abf828a6d0e87d3385680eaf830cd60d20d52f249faabf39"}, + {file = "greenlet-3.3.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:a1e41a81c7e2825822f4e068c48cb2196002362619e2d70b148f20a831c00739"}, + {file = "greenlet-3.3.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9f515a47d02da4d30caaa85b69474cec77b7929b2e936ff7fb853d42f4bf8808"}, + {file = "greenlet-3.3.0-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7d2d9fd66bfadf230b385fdc90426fcd6eb64db54b40c495b72ac0feb5766c54"}, + {file = "greenlet-3.3.0-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:30a6e28487a790417d036088b3bcb3f3ac7d8babaa7d0139edbaddebf3af9492"}, + {file = "greenlet-3.3.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:087ea5e004437321508a8d6f20efc4cfec5e3c30118e1417ea96ed1d93950527"}, + {file = "greenlet-3.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ab97cf74045343f6c60a39913fa59710e4bd26a536ce7ab2397adf8b27e67c39"}, + {file = "greenlet-3.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5375d2e23184629112ca1ea89a53389dddbffcf417dad40125713d88eb5f96e8"}, + {file = "greenlet-3.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:9ee1942ea19550094033c35d25d20726e4f1c40d59545815e1128ac58d416d38"}, + {file = "greenlet-3.3.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:60c2ef0f578afb3c8d92ea07ad327f9a062547137afe91f38408f08aacab667f"}, + {file = "greenlet-3.3.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a5d554d0712ba1de0a6c94c640f7aeba3f85b3a6e1f2899c11c2c0428da9365"}, + {file = "greenlet-3.3.0-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3a898b1e9c5f7307ebbde4102908e6cbfcb9ea16284a3abe15cab996bee8b9b3"}, + {file = "greenlet-3.3.0-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:dcd2bdbd444ff340e8d6bdf54d2f206ccddbb3ccfdcd3c25bf4afaa7b8f0cf45"}, + {file = "greenlet-3.3.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5773edda4dc00e173820722711d043799d3adb4f01731f40619e07ea2750b955"}, + {file = "greenlet-3.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ac0549373982b36d5fd5d30beb8a7a33ee541ff98d2b502714a09f1169f31b55"}, + {file = "greenlet-3.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d198d2d977460358c3b3a4dc844f875d1adb33817f0613f663a656f463764ccc"}, + {file = "greenlet-3.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:73f51dd0e0bdb596fb0417e475fa3c5e32d4c83638296e560086b8d7da7c4170"}, + {file = "greenlet-3.3.0-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:d6ed6f85fae6cdfdb9ce04c9bf7a08d666cfcfb914e7d006f44f840b46741931"}, + {file = "greenlet-3.3.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d9125050fcf24554e69c4cacb086b87b3b55dc395a8b3ebe6487b045b2614388"}, + {file = "greenlet-3.3.0-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:87e63ccfa13c0a0f6234ed0add552af24cc67dd886731f2261e46e241608bee3"}, + {file = "greenlet-3.3.0-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2662433acbca297c9153a4023fe2161c8dcfdcc91f10433171cf7e7d94ba2221"}, + {file = "greenlet-3.3.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3c6e9b9c1527a78520357de498b0e709fb9e2f49c3a513afd5a249007261911b"}, + {file = "greenlet-3.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:286d093f95ec98fdd92fcb955003b8a3d054b4e2cab3e2707a5039e7b50520fd"}, + {file = "greenlet-3.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c10513330af5b8ae16f023e8ddbfb486ab355d04467c4679c5cfe4659975dd9"}, + {file = "greenlet-3.3.0.tar.gz", hash = "sha256:a82bb225a4e9e4d653dd2fb7b8b2d36e4fb25bc0165422a11e48b88e9e6f78fb"}, +] + +[package.extras] +docs = ["Sphinx", "furo"] +test = ["objgraph", "psutil", "setuptools"] + [[package]] name = "h11" version = "0.16.0" @@ -2048,6 +2131,26 @@ cli = ["jsonargparse[signatures] (>=4.38.0)", "tomlkit"] docs = ["requests (>=2.0.0)"] typing = ["mypy (>=1.0.0)", "types-setuptools"] +[[package]] +name = "mako" +version = "1.3.10" +description = "A super-fast templating language that borrows the best ideas from the existing templating languages." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "mako-1.3.10-py3-none-any.whl", hash = "sha256:baef24a52fc4fc514a0887ac600f9f1cff3d82c61d4d700a1fa84d597b88db59"}, + {file = "mako-1.3.10.tar.gz", hash = "sha256:99579a6f39583fa7e5630a28c3c1f440e4e97a414b80372649c0ce338da2ea28"}, +] + +[package.dependencies] +MarkupSafe = ">=0.9.2" + +[package.extras] +babel = ["Babel"] +lingua = ["lingua"] +testing = ["pytest"] + [[package]] name = "markdown-it-py" version = "4.0.0" @@ -2968,6 +3071,33 @@ files = [ {file = "nvidia_nvtx_cu12-12.8.90-py3-none-win_amd64.whl", hash = "sha256:619c8304aedc69f02ea82dd244541a83c3d9d40993381b3b590f1adaed3db41e"}, ] +[[package]] +name = "optuna" +version = "4.7.0" +description = "A hyperparameter optimization framework" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "optuna-4.7.0-py3-none-any.whl", hash = "sha256:e41ec84018cecc10eabf28143573b1f0bde0ba56dba8151631a590ecbebc1186"}, + {file = "optuna-4.7.0.tar.gz", hash = "sha256:d91817e2079825557bd2e97de2e8c9ae260bfc99b32712502aef8a5095b2d2c0"}, +] + +[package.dependencies] +alembic = ">=1.5.0" +colorlog = "*" +numpy = "*" +packaging = ">=20.0" +PyYAML = "*" +sqlalchemy = ">=1.4.2" +tqdm = "*" + +[package.extras] +checking = ["mypy", "mypy_boto3_s3", "ruff", "scipy-stubs ; python_version >= \"3.10\"", "types-PyYAML", "types-redis", "types-setuptools", "types-tqdm", "typing_extensions (>=3.10.0.0)"] +document = ["ase", "cmaes (>=0.12.0)", "fvcore", "kaleido (<0.4)", "lightgbm", "matplotlib (!=3.6.0)", "pandas", "pillow", "plotly (>=4.9.0)", "scikit-learn", "sphinx", "sphinx-copybutton", "sphinx-gallery", "sphinx-notfound-page", "sphinx_rtd_theme (>=1.2.0)", "torch", "torchvision"] +optional = ["boto3", "cmaes (>=0.12.0)", "google-cloud-storage", "greenlet", "grpcio", "matplotlib (!=3.6.0)", "pandas", "plotly (>=4.9.0)", "protobuf (>=5.28.1)", "redis", "scikit-learn (>=0.24.2)", "scipy", "torch"] +test = ["fakeredis[lua]", "greenlet", "grpcio", "kaleido (<0.4)", "moto", "protobuf (>=5.28.1)", "pytest", "pytest-xdist", "scipy (>=1.9.2)", "torch"] + [[package]] name = "packaging" version = "26.0" @@ -5145,6 +5275,104 @@ lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] standalone = ["Sphinx (>=5)"] test = ["pytest"] +[[package]] +name = "sqlalchemy" +version = "2.0.46" +description = "Database Abstraction Library" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "sqlalchemy-2.0.46-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:895296687ad06dc9b11a024cf68e8d9d3943aa0b4964278d2553b86f1b267735"}, + {file = "sqlalchemy-2.0.46-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ab65cb2885a9f80f979b85aa4e9c9165a31381ca322cbde7c638fe6eefd1ec39"}, + {file = "sqlalchemy-2.0.46-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:52fe29b3817bd191cc20bad564237c808967972c97fa683c04b28ec8979ae36f"}, + {file = "sqlalchemy-2.0.46-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:09168817d6c19954d3b7655da6ba87fcb3a62bb575fb396a81a8b6a9fadfe8b5"}, + {file = "sqlalchemy-2.0.46-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:be6c0466b4c25b44c5d82b0426b5501de3c424d7a3220e86cd32f319ba56798e"}, + {file = "sqlalchemy-2.0.46-cp310-cp310-win32.whl", hash = "sha256:1bc3f601f0a818d27bfe139f6766487d9c88502062a2cd3a7ee6c342e81d5047"}, + {file = "sqlalchemy-2.0.46-cp310-cp310-win_amd64.whl", hash = "sha256:e0c05aff5c6b1bb5fb46a87e0f9d2f733f83ef6cbbbcd5c642b6c01678268061"}, + {file = "sqlalchemy-2.0.46-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:261c4b1f101b4a411154f1da2b76497d73abbfc42740029205d4d01fa1052684"}, + {file = "sqlalchemy-2.0.46-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:181903fe8c1b9082995325f1b2e84ac078b1189e2819380c2303a5f90e114a62"}, + {file = "sqlalchemy-2.0.46-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:590be24e20e2424a4c3c1b0835e9405fa3d0af5823a1a9fc02e5dff56471515f"}, + {file = "sqlalchemy-2.0.46-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7568fe771f974abadce52669ef3a03150ff03186d8eb82613bc8adc435a03f01"}, + {file = "sqlalchemy-2.0.46-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ebf7e1e78af38047e08836d33502c7a278915698b7c2145d045f780201679999"}, + {file = "sqlalchemy-2.0.46-cp311-cp311-win32.whl", hash = "sha256:9d80ea2ac519c364a7286e8d765d6cd08648f5b21ca855a8017d9871f075542d"}, + {file = "sqlalchemy-2.0.46-cp311-cp311-win_amd64.whl", hash = "sha256:585af6afe518732d9ccd3aea33af2edaae4a7aa881af5d8f6f4fe3a368699597"}, + {file = "sqlalchemy-2.0.46-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3a9a72b0da8387f15d5810f1facca8f879de9b85af8c645138cba61ea147968c"}, + {file = "sqlalchemy-2.0.46-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2347c3f0efc4de367ba00218e0ae5c4ba2306e47216ef80d6e31761ac97cb0b9"}, + {file = "sqlalchemy-2.0.46-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9094c8b3197db12aa6f05c51c05daaad0a92b8c9af5388569847b03b1007fb1b"}, + {file = "sqlalchemy-2.0.46-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37fee2164cf21417478b6a906adc1a91d69ae9aba8f9533e67ce882f4bb1de53"}, + {file = "sqlalchemy-2.0.46-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b1e14b2f6965a685c7128bd315e27387205429c2e339eeec55cb75ca4ab0ea2e"}, + {file = "sqlalchemy-2.0.46-cp312-cp312-win32.whl", hash = "sha256:412f26bb4ba942d52016edc8d12fb15d91d3cd46b0047ba46e424213ad407bcb"}, + {file = "sqlalchemy-2.0.46-cp312-cp312-win_amd64.whl", hash = "sha256:ea3cd46b6713a10216323cda3333514944e510aa691c945334713fca6b5279ff"}, + {file = "sqlalchemy-2.0.46-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:93a12da97cca70cea10d4b4fc602589c4511f96c1f8f6c11817620c021d21d00"}, + {file = "sqlalchemy-2.0.46-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:af865c18752d416798dae13f83f38927c52f085c52e2f32b8ab0fef46fdd02c2"}, + {file = "sqlalchemy-2.0.46-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8d679b5f318423eacb61f933a9a0f75535bfca7056daeadbf6bd5bcee6183aee"}, + {file = "sqlalchemy-2.0.46-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:64901e08c33462acc9ec3bad27fc7a5c2b6491665f2aa57564e57a4f5d7c52ad"}, + {file = "sqlalchemy-2.0.46-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e8ac45e8f4eaac0f9f8043ea0e224158855c6a4329fd4ee37c45c61e3beb518e"}, + {file = "sqlalchemy-2.0.46-cp313-cp313-win32.whl", hash = "sha256:8d3b44b3d0ab2f1319d71d9863d76eeb46766f8cf9e921ac293511804d39813f"}, + {file = "sqlalchemy-2.0.46-cp313-cp313-win_amd64.whl", hash = "sha256:77f8071d8fbcbb2dd11b7fd40dedd04e8ebe2eb80497916efedba844298065ef"}, + {file = "sqlalchemy-2.0.46-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a1e8cc6cc01da346dc92d9509a63033b9b1bda4fed7a7a7807ed385c7dccdc10"}, + {file = "sqlalchemy-2.0.46-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:96c7cca1a4babaaf3bfff3e4e606e38578856917e52f0384635a95b226c87764"}, + {file = "sqlalchemy-2.0.46-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b2a9f9aee38039cf4755891a1e50e1effcc42ea6ba053743f452c372c3152b1b"}, + {file = "sqlalchemy-2.0.46-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:db23b1bf8cfe1f7fda19018e7207b20cdb5168f83c437ff7e95d19e39289c447"}, + {file = "sqlalchemy-2.0.46-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:56bdd261bfd0895452006d5316cbf35739c53b9bb71a170a331fa0ea560b2ada"}, + {file = "sqlalchemy-2.0.46-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:33e462154edb9493f6c3ad2125931e273bbd0be8ae53f3ecd1c161ea9a1dd366"}, + {file = "sqlalchemy-2.0.46-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9bcdce05f056622a632f1d44bb47dbdb677f58cad393612280406ce37530eb6d"}, + {file = "sqlalchemy-2.0.46-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8e84b09a9b0f19accedcbeff5c2caf36e0dd537341a33aad8d680336152dc34e"}, + {file = "sqlalchemy-2.0.46-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4f52f7291a92381e9b4de9050b0a65ce5d6a763333406861e33906b8aa4906bf"}, + {file = "sqlalchemy-2.0.46-cp314-cp314-win32.whl", hash = "sha256:70ed2830b169a9960193f4d4322d22be5c0925357d82cbf485b3369893350908"}, + {file = "sqlalchemy-2.0.46-cp314-cp314-win_amd64.whl", hash = "sha256:3c32e993bc57be6d177f7d5d31edb93f30726d798ad86ff9066d75d9bf2e0b6b"}, + {file = "sqlalchemy-2.0.46-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4dafb537740eef640c4d6a7c254611dca2df87eaf6d14d6a5fca9d1f4c3fc0fa"}, + {file = "sqlalchemy-2.0.46-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:42a1643dc5427b69aca967dae540a90b0fbf57eaf248f13a90ea5930e0966863"}, + {file = "sqlalchemy-2.0.46-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ff33c6e6ad006bbc0f34f5faf941cfc62c45841c64c0a058ac38c799f15b5ede"}, + {file = "sqlalchemy-2.0.46-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:82ec52100ec1e6ec671563bbd02d7c7c8d0b9e71a0723c72f22ecf52d1755330"}, + {file = "sqlalchemy-2.0.46-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6ac245604295b521de49b465bab845e3afe6916bcb2147e5929c8041b4ec0545"}, + {file = "sqlalchemy-2.0.46-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e6199143d51e3e1168bedd98cc698397404a8f7508831b81b6a29b18b051069"}, + {file = "sqlalchemy-2.0.46-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:716be5bcabf327b6d5d265dbdc6213a01199be587224eb991ad0d37e83d728fd"}, + {file = "sqlalchemy-2.0.46-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:6f827fd687fa1ba7f51699e1132129eac8db8003695513fcf13fc587e1bd47a5"}, + {file = "sqlalchemy-2.0.46-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c805fa6e5d461329fa02f53f88c914d189ea771b6821083937e79550bf31fc19"}, + {file = "sqlalchemy-2.0.46-cp38-cp38-win32.whl", hash = "sha256:3aac08f7546179889c62b53b18ebf1148b10244b3405569c93984b0388d016a7"}, + {file = "sqlalchemy-2.0.46-cp38-cp38-win_amd64.whl", hash = "sha256:0cc3117db526cad3e61074100bd2867b533e2c7dc1569e95c14089735d6fb4fe"}, + {file = "sqlalchemy-2.0.46-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:90bde6c6b1827565a95fde597da001212ab436f1b2e0c2dcc7246e14db26e2a3"}, + {file = "sqlalchemy-2.0.46-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:94b1e5f3a5f1ff4f42d5daab047428cd45a3380e51e191360a35cef71c9a7a2a"}, + {file = "sqlalchemy-2.0.46-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:93bb0aae40b52c57fd74ef9c6933c08c040ba98daf23ad33c3f9893494b8d3ce"}, + {file = "sqlalchemy-2.0.46-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:c4e2cc868b7b5208aec6c960950b7bb821f82c2fe66446c92ee0a571765e91a5"}, + {file = "sqlalchemy-2.0.46-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:965c62be8256d10c11f8907e7a8d3e18127a4c527a5919d85fa87fd9ecc2cfdc"}, + {file = "sqlalchemy-2.0.46-cp39-cp39-win32.whl", hash = "sha256:9397b381dcee8a2d6b99447ae85ea2530dcac82ca494d1db877087a13e38926d"}, + {file = "sqlalchemy-2.0.46-cp39-cp39-win_amd64.whl", hash = "sha256:4396c948d8217e83e2c202fbdcc0389cf8c93d2c1c5e60fa5c5a955eae0e64be"}, + {file = "sqlalchemy-2.0.46-py3-none-any.whl", hash = "sha256:f9c11766e7e7c0a2767dda5acb006a118640c9fc0a4104214b96269bfb78399e"}, + {file = "sqlalchemy-2.0.46.tar.gz", hash = "sha256:cf36851ee7219c170bb0793dbc3da3e80c582e04a5437bc601bfe8c85c9216d7"}, +] + +[package.dependencies] +greenlet = {version = ">=1", markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\""} +typing-extensions = ">=4.6.0" + +[package.extras] +aiomysql = ["aiomysql (>=0.2.0)", "greenlet (>=1)"] +aioodbc = ["aioodbc", "greenlet (>=1)"] +aiosqlite = ["aiosqlite", "greenlet (>=1)", "typing_extensions (!=3.10.0.1)"] +asyncio = ["greenlet (>=1)"] +asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (>=1)"] +mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5,!=1.1.10)"] +mssql = ["pyodbc"] +mssql-pymssql = ["pymssql"] +mssql-pyodbc = ["pyodbc"] +mypy = ["mypy (>=0.910)"] +mysql = ["mysqlclient (>=1.4.0)"] +mysql-connector = ["mysql-connector-python"] +oracle = ["cx_oracle (>=8)"] +oracle-oracledb = ["oracledb (>=1.0.1)"] +postgresql = ["psycopg2 (>=2.7)"] +postgresql-asyncpg = ["asyncpg", "greenlet (>=1)"] +postgresql-pg8000 = ["pg8000 (>=1.29.1)"] +postgresql-psycopg = ["psycopg (>=3.0.7)"] +postgresql-psycopg2binary = ["psycopg2-binary"] +postgresql-psycopg2cffi = ["psycopg2cffi"] +postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"] +pymysql = ["pymysql"] +sqlcipher = ["sqlcipher3_binary"] + [[package]] name = "starlette" version = "0.52.1" @@ -6402,4 +6630,4 @@ multiprocessing = ["pydantic", "ray"] [metadata] lock-version = "2.1" python-versions = ">=3.11,<3.14" -content-hash = "b48d33e2c6e66c3fa8aa5b42db423e8577a44e19090b766f65051f4b9587dde4" +content-hash = "205db54ef1c6b5ee0b5d9821e6bc9f10d020e674c71fe62a03a477c226ab0b47" diff --git a/pyproject.toml b/pyproject.toml index d6056275..c8a0b56a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,6 +50,7 @@ poetry = "^2.0.1" starlette = ">=0.49.1" pydantic = { version = ">=2.5", optional = true } wandb = "^0.24.0" +optuna = "^4.7.0" [tool.poetry.requires-plugins] poetry-plugin-export = ">=1.8"