Skip to content

configspace.update for hyperparameter settings#414

Open
thijssnelleman wants to merge 14 commits into
mainfrom
configspace.update
Open

configspace.update for hyperparameter settings#414
thijssnelleman wants to merge 14 commits into
mainfrom
configspace.update

Conversation

@thijssnelleman
Copy link
Copy Markdown
Collaborator

#412

This is a proposed solution to this issue; Hyperparameters have properties that are not updated by changing the values. By adapting the updating functionality, it first tests if there already is a value present, and if so, verifies what needs to be recalculated.

  1. Assumes that in the initialisation all values are set exactly once, otherwise the update will be triggered and may cause issues when properties are accessed that do not exist yet
  2. Shares a lot of code with that found in the init function. Would be better if this would be settled in one function rather than spread over two
  3. Needs to be adapted for each HP type. Need to find out if we can do this more generally (For example, what can be done in the parent Hyperparameter class? Not everything but maybe something like default value?)

Have written a partial pytest to verify the behaviour, seems to work just fine with this suggested solution

Copy link
Copy Markdown
Contributor

@eddiebergman eddiebergman left a comment

Choose a reason for hiding this comment

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

Might be a lot easier to just reconstruct the whole hyperparameter, and then steal it's values. This means you don't have to duplicate validation, and you can use this method with minor adjustments on everywhere.

Read up on dataclasses. There is a method signature .replace() -> Self which will basically do this, but it returns a new instance.

Just take special note that we define a custom __init__ for dataclasses, which is not normally what you should do. This means you don't get all their benefits. They exist to be backwards compatbile with the public API

def __setattr__(self, name: str, value: Any):
    init_params: dict[str, Any] = # get init params
    if name not in init_params:
        raise ValueError("Can't set attribute {name}, must be one passed to init.") # Something better error message than this
    init_params[name] = value
    
    # This raises if invalid
    new_instance = self.__class__(**init_params)
    self.__dict__ = new_instance.__dict__ # Unsure if this works exactly

@thijssnelleman
Copy link
Copy Markdown
Collaborator Author

That is actually a good idea, although it could be inefficient in some cases. I went with a slightly different strategy; the setattr has now a different workflow if a value is not being set from init. The check is quite ugly, but works. Instead of using replace, I am just calling self.init again. Although current tests are basics, they are passing, wondering what @eddiebergman thinks of this solution.

@thijssnelleman
Copy link
Copy Markdown
Collaborator Author

@eddiebergman

That is actually a good idea, although it could be inefficient in some cases. I went with a slightly different strategy; the setattr has now a different workflow if a value is not being set from init. The check is quite ugly, but works. Instead of using replace, I am just calling self.init again. Although current tests are basics, they are passing, wondering what @eddiebergman thinks of this solution.

Does this solutions stray too far from your initial idea?

@thijssnelleman
Copy link
Copy Markdown
Collaborator Author

@mfeurer Reminder for this PR

@thijssnelleman thijssnelleman requested a review from karibbov April 10, 2026 07:51
Copy link
Copy Markdown
Collaborator

@karibbov karibbov left a comment

Choose a reason for hiding this comment

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

Summary:
This PR:

  1. Prevents direct (assignment) attribute setting from anywhere except from within any function named __init__.
  2. Then allows only the attributes that has the same name as an initialization parameter, by reinitializing from those attributes. This implies all the initialization parameters must be saved without modification at every HP.__init__ implementation. This is not as strict of a condition as it seems, since all HPs are dataclasses. However I think we should document it somewhere visible since we always have a custom __init__ function.

Suggestion

  1. Mainly documetation (see comments)
  2. Can we include an example test for all the other init parameters (attributes) being the same before and after an attribute is reset? This is to catch the case when some HP.__init__ saves a modified version of the init parameter as an attribute.

Comment thread src/ConfigSpace/hyperparameters/hyperparameter.py Outdated
Comment thread src/ConfigSpace/hyperparameters/hyperparameter.py Outdated
Comment thread src/ConfigSpace/hyperparameters/hyperparameter.py Outdated
Comment thread src/ConfigSpace/hyperparameters/hyperparameter.py Outdated
Comment thread test/test_hyperparameters.py
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Implements a first-pass solution for dynamically updating hyperparameter bounds/defaults by re-initializing hyperparameter instances on attribute assignment, addressing ConfigSpace issue #412 (online updates/search space shrinking).

Changes:

  • Add Hyperparameter.__setattr__ logic to restrict post-init mutations to __init__ parameters and to re-run __init__ when such attributes are updated.
  • Standardize several “illegal settings” error messages to “Illegal value(s) for Hyperparameter …” and adjust tests accordingly.
  • Add a new pytest covering runtime updates for integer/float/categorical/ordinal hyperparameters and verifying sampling stays in-bounds.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
src/ConfigSpace/hyperparameters/hyperparameter.py Adds update-via-reinit behavior in __setattr__ and fixes a typo in an error message.
src/ConfigSpace/hyperparameters/uniform_integer.py Updates illegal-bounds error message text.
src/ConfigSpace/hyperparameters/uniform_float.py Updates illegal-bounds error message text.
src/ConfigSpace/hyperparameters/normal_integer.py Updates error message text for invalid params and bounds.
src/ConfigSpace/hyperparameters/normal_float.py Updates error message text for invalid params and bounds.
src/ConfigSpace/hyperparameters/beta_integer.py Updates illegal-bounds error message text.
src/ConfigSpace/hyperparameters/beta_float.py Updates illegal-bounds error message text.
test/test_hyperparameters.py Updates error-message matching and adds a new test validating runtime hyperparameter updates.
docs/reference/hyperparameters.md Documents the new update behavior and aligns an example snippet’s error message.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread test/test_hyperparameters.py
Comment thread test/test_hyperparameters.py Outdated
Comment thread src/ConfigSpace/hyperparameters/hyperparameter.py
Comment thread src/ConfigSpace/hyperparameters/hyperparameter.py Outdated
Comment thread src/ConfigSpace/hyperparameters/hyperparameter.py
Comment thread docs/reference/hyperparameters.md Outdated
thijssnelleman and others added 3 commits May 18, 2026 10:12
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@thijssnelleman thijssnelleman changed the title Example solution to adapting HP bounds and defaults dynamically configspace.update for hyperparameter settings May 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants