Skip to content

Conversation

@Meganton
Copy link
Collaborator

This pull request updates the documentation to standardize and modernize how search spaces are defined and referenced in NePS. The main change is moving from dictionary-based search space definitions to using classes that inherit from neps.PipelineSpace.

Migration to class-based search space definitions:

  • The new spaces include HPO but also general CFG-like sampling capabilities. See the new docs for more information.
  • Updated all examples in docs/getting_started.md, docs/index.md, and docs/reference/neps_run.md to use a class inheriting from neps.PipelineSpace instead of a dictionary for defining the search space. This includes updating parameter definitions to use neps.Float, neps.Integer, and neps.Categorical with new argument names like min_value and max_value. [1] [2] [3]

Reference and terminology updates:

  • Updated method calls to use the new class-based search space, e.g., neps.run(evaluate_pipeline, ExampleSpace()) instead of passing a dictionary. [1] [2] [3]

Minor formatting and clarity improvements:

  • Clarified argument names and usage in code samples to align with the updated API. [1] [2] [3]

lumib and others added 30 commits April 14, 2025 18:44
- Introduced tests for various aspects of the NEPS space, including:
  - `test_search_space__nos_like.py`: Tests for the resolution of a complex pipeline with unary, binary, and ternary operations.
  - `test_search_space__recursion.py`: Validates recursive operations within the pipeline, ensuring correct factor propagation.
  - `test_search_space__resampled.py`: Tests for resampling behavior in both float and integer pipelines, ensuring shared and unique values are handled correctly.
  - `test_search_space__reuse_arch_elements.py`: Verifies the reuse of architectural elements in pipelines, ensuring correct operation and configuration string generation.
  - `utils.py`: Added utility functions to generate possible configuration strings for pipelines, aiding in testing and validation.
…odule

- Changed all instances of `space` to `neps_space` in test files to reflect the new import structure.
- Updated class definitions to inherit from `neps_space.Pipeline` instead of `space.Pipeline`.
- Modified the creation of operations and categorical choices to use `neps_space.Operation` and `neps_space.Categorical`.
- Adjusted the resolution and conversion functions to utilize the new `neps_space` methods.
- Ensured consistency across all test cases for the new structure and functionality.
- Added a new module `bracket_optimizer.py` that implements a bracket-based optimization strategy for multi-fidelity configurations in NePS spaces.
- Introduced the `_BracketOptimizer` class to handle the optimization process, including sampling configurations from a prior band and managing different fidelity levels.
- Implemented various bracket types such as successive halving, hyperband, ASHA, and async hyperband.
- Updated `priorband.py` to utilize the new sampling strategies and refactored the prior sampling logic.
- Created a new `sampling.py` module to define various samplers, including `OnlyPredefinedValuesSampler`, `RandomSampler`, `PriorOrFallbackSampler`, and mutation-based samplers.
- Modified existing tests to accommodate the new optimizer and sampling strategies, ensuring compatibility with the updated structure.
- Refactored import paths in test files to align with the new module organization.
- Updated import statements to use `neps.space.neps_spaces.parameters` for all relevant classes and functions in test files.
- Refactored test classes and methods to replace direct usage of `neps_space` with the new parameters module, ensuring consistency across the test suite.
- Adjusted the instantiation of various operations and categorical choices to align with the new structure.
- Ensured that all references to operations, floats, integers, and categorical choices are correctly sourced from the updated parameters module.
- Updated imports in test files to directly import necessary classes from neps.space.neps_spaces.parameters.
- Refactored class definitions to inherit directly from Pipeline and other relevant classes.
- Replaced instances of parameter class references with direct class references for improved readability.
- Ensured consistency across test files by applying similar refactoring patterns.
- Updated imports for optimizers in test_neps_integration.py to use the new neps.optimizers module.
- Replaced references to old RandomSearch and ComplexRandomSearch algorithms with their new counterparts from neps_algorithms.
- Adjusted the optimizer imports in test_neps_integration_priorband__max_cost.py and test_neps_integration_priorband__max_evals.py to align with the new structure.
- Ensured all instances of priorband optimizers are updated to use the new neps_algorithms module.
…eration classes to improve clarity and consistency
…arity and consistency

- Updated docstrings in SamplingResolutionContext to use Args and Returns sections for better readability.
- Changed parameter names from `prior_index` to `prior` in Categorical class to enhance clarity.
- Introduced a new function `convert_confidence_level` to handle string to ConfidenceLevel conversion.
- Updated tests to reflect changes in parameter names from `prior_index` to `prior`.
- Enhanced docstrings across various classes and methods in the sampling module for consistency.
…ing clarity, and improving formatting in code examples
… HPO classes

- Updated the image segmentation example to utilize HPO_Float and HPO_Integer for hyperparameter definitions.
- Refactored test cases to replace old parameter classes with HPO variants, ensuring compatibility with the new hyperparameter optimization framework.
- Added log sampling for NePS-Integer
- Adjusted imports in test files to reflect changes in the neps library structure.
- Enhanced the configuration encoder tests to validate the new HPO parameter types.
- Ensured all relevant tests are passing with the updated hyperparameter definitions.
…md file

- Corrected links in getting_started.md, evaluate_pipeline.md, and neps_run.md to point to neps_spaces.md.
- Updated navigation in mkdocs.yml to reflect the removal of pipeline_space.md.
- Enhanced clarity in algorithms.py and grid.py by updating parameter references.
- Added validation in parameters.py to ensure prior_confidence is set when prior is defined.
…n neps.run() and enhance fidelity parameter explanation
…thods to avoid using a cached resolved object instead of resolving when they are actually different
…y when required and improve DataFrame handling
… metrics functionality

- Introduced tests for converting between classic SearchSpace and NePS PipelineSpace, ensuring attributes and properties are preserved.
- Implemented tests for algorithm compatibility with both NePS-only and classic algorithms.
- Added tests for trajectory and metrics functionality, including handling of fidelity parameters and cumulative metrics tracking.
- Included error handling tests for failed evaluations and validation of metric values.
- Verified file format and structure for trajectory and best config outputs.
- Ensured that NePS can revisit previous runs and update trajectories correctly.
…cripts

- Deleted `priors_test.ipynb` and `warmstarting.py` as they were no longer needed.
…egration tests for hyperparameter optimization with fidelity
-> Add trial validation and import functionality

- Introduced `TrialValidationError` exception for handling trial configuration validation errors.
- Created a new module `normalization.py` for normalizing imported configurations.
- Implemented `import_trials` method in various optimizers (e.g., `BayesianOptimization`, `BracketOptimizer`, `GridSearch`, `IFBO`, `PriMO`, `RandomSearch`) to handle external evaluations.
- Added validation functions in `validation.py` to ensure imported results and configurations meet expected criteria.
- Enhanced `Trial` class to support loading from external data with a new `load_from_dict` method.
- Updated `BudgetInfo` and `WorkerSettings` to allow for float values in `fidelities_to_spend`.
- Added utility functions in `trial_io.py` for loading trials from pickle files.
- Created an example script `example_import_trials.py` to demonstrate the import trials functionality.
HPOInteger,
SearchSpace,
)
from neps.space.neps_spaces import neps_space
Copy link
Collaborator

@nastaran78 nastaran78 Oct 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: if we are exposing the specific functions from neps_space to user-side, it's it better to make it accessible from neps directly? if it's a util function which is not the main functionality and not used in the happy path but will give insight to user I'd suggest to put it in neps.utils

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which specific functions do you mean should be moved to util?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but moved, but maybe imported in the init of utils package, I was thinking that convert_operation_to_string is not something that user may need normally.

ValueError: If the resampling is by name or the source is not resolvable.
"""
if self.is_resampling_by_name:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DRY: the validation part for both get_attrs and from_attrs are the same, potentially we could extract a _get_resolvable_source function.

return f"Resampled({self._source!s})"

@property
def source(self) -> Resolvable | str:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: we can get rid of the source property and rename _source to source if we don't have extra steps here in getter.

(Resampled("model"), Resampled(_C)),
(Resampled(_O), Resampled(_O), Resampled(_O)),
(
Resampled("model"),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose this has a high potential for infinite recursion. Python's call stack exceeds its limit (1000 by default). I encountered that 2 times (however the odds should be low by theory). Maybe we can validate that our grammar is a DAG?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, we specifically want these recursions to be possible. What does Neps do when encountering an error during sampling? Would it just resample?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we have a 10 time retry in the runtime function when catching an exception.
I don't get any Neps error, I get the error from system, which we cannot prevent if we can't have a validation like that, that's sad.

import torch
import torch.nn as nn
import neps
from neps.space.neps_spaces.parameters import (
Copy link
Collaborator

@nastaran78 nastaran78 Oct 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use the exposed neps.PipelineSpace

Comment on lines +249 to +251
converted_space = convert_neps_to_classic_search_space(pipeline_space)
if converted_space is not None:
pipeline_space = converted_space
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
converted_space = convert_neps_to_classic_search_space(pipeline_space)
if converted_space is not None:
pipeline_space = converted_space
pipeline_space = convert_neps_to_classic_search_space(pipeline_space)
if pipeline_space is None:
raise ...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if this works everywhere, as we sometimes proceed with the old space to run an old optimizer. I think it's better to keep them separated.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand your point, but here we are assigning that to pipeline_space anyways if it's not None and otherwise raising error.

value.prior_confidence.value if value.has_prior else "low"
),
)
elif isinstance(value, Fidelity):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it doesn't reach this condition, because we don't have the Fidelity attributes in get_attrs().
I believe, we should separately iterate over fidelity_attrs().

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, i missed that, thanks. Don't understand why it worked anyway, but yeah we can just call fidelity_attrs and add it to the dict we're iterating over

environment_values=sampled_pipeline_data.environment_values,
)

config = dict(**sampled_pipeline.get_attrs())
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's better to have get_attr more general containing fidelities. maybe we can have a flag as an argument whether we want the fidelity parameters or not.
here also I think our intention is to include fidelity.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

4 participants