How to Define a Complete Configuration Space with Hyperparameter Specifications in Hypster
You define a complete configuration space in Hypster by writing a Python function that uses the HP object to declare parameters with types, bounds, and optional HPO metadata, then calling instantiate() to validate and materialize concrete values.
The Hypster library (gilad-rubin/hypster) provides a declarative API for building configuration spaces that bridge the gap between static configuration files and hyperparameter optimization frameworks like Optuna. By leveraging Python functions and the HP class, you can encode complex parameter relationships, validation rules, and search space definitions in a single, readable structure.
The Core Architecture of Hypster Configuration Spaces
Hypster's architecture consists of several coordinated layers that transform a user-defined configuration function into a validated, optionally optimizable parameter space.
The instantiate Entry Point
The primary entry point in [src/hypster/core.py](https://github.com/gilad-rubin/hypster/blob/master/src/hypster/core.py#L17) orchestrates the configuration flow. The instantiate(config_func, values=...) function:
- Validates the signature of your configuration function
- Creates an
HPinstance populated with supplied values - Executes the function to build the configuration space
- Checks for unknown parameters via
_handle_unknown_parameters(lines 73-106)
This final validation step can warn, raise, or ignore unused keys and even suggests similar names using suggest_similar_names from [src/hypster/utils.py](https://github.com/gilad-rubin/hypster/blob/master/src/hypster/utils.py).
The HP Class API
The HP class in src/hypster/hp.py provides the public interface for parameter declaration. Key methods include:
int– Integer parameters with optional min/max boundsfloat– Floating-point values with precision controlselect– Single choice from a list of optionsmulti_int,multi_float,multi_text,multi_bool– Multi-value parameterstext– String parametersbool– Boolean flagsnest– Nested configuration spaces
Each method delegates to specification objects (SingleValueSpec, MultiValueSpec, SelectSingleSpec, SelectMultiSpec) and low-level handlers that manage parameter registration.
Validation and Type Safety
The validators in [src/hypster/hp_calls.py](https://github.com/gilad-rubin/hypster/blob/master/src/hypster/hp_calls.py) enforce constraints at definition time:
IntValidatorandFloatValidatorperform bounds checking viavalidate_boundsto ensure values satisfymin/maxconstraintsSelectValidatorvalidates categorical choices against available optionsMultiValidatorhandles multi-value parameter validation
The optional strict flag prevents silent type down-casting, while name validation ensures the name= parameter is supplied when using value overrides.
HPO Metadata Definitions
For hyperparameter optimization, src/hypster/hpo/types.py defines data classes that extend parameters with search space metadata:
HpoInt– Configures step sizes, logarithmic scaling (scale="log"), and inclusive-max flagsHpoFloat– Supports step quantization, scaling modes, and distributions likeloguniformHpoCategorical– Enables ordered categorical flags and optional weight vectors for sampling bias
Pass these as the hpo_spec= argument to any HP method to enable Optuna integration.
Optuna Integration via _HPProxy
When optimizing, the _HPProxy class in src/hypster/hpo/optuna.py mirrors the HP API but forwards each call to trial.suggest_*. The proxy records suggested values in a collector dictionary, allowing you to retrieve the exact sampled configuration after the trial completes.
Namespace Handling with Nested Configurations
The nest method creates hierarchical parameter spaces by instantiating a new HP object with an extended namespace (parent.child.param). This mechanism:
- Tracks nested calls to prevent name collisions
- Propagates parameter access information upward
- Enables conditional configuration blocks
Use nesting to create modular, reusable configuration components that merge into a unified space.
Step-by-Step: Building a Configuration Function
Define your configuration space by writing a function that accepts an HP instance and declares parameters using the methods above:
# file: my_config.py
from hypster import HP
from hypster.hpo.types import HpoInt, HpoFloat, HpoCategorical
def model_cfg(hp: HP):
# Integer with bounds and HPO step specification
batch_size = hp.int(
32,
name="batch_size",
min=8,
max=256,
hpo_spec=HpoInt(step=8, scale="log")
)
# Float with log-uniform distribution for optimization
lr = hp.float(
0.001,
name="learning_rate",
min=1e-5,
max=1e-1,
hpo_spec=HpoFloat(scale="log", distribution="loguniform")
)
# Categorical selection with sampling weights
optimizer = hp.select(
["adam", "sgd", "rmsprop"],
name="optimizer",
default="adam",
hpo_spec=HpoCategorical(weights=[0.6, 0.3, 0.1])
)
# Multi-boolean flags
flags = hp.multi_bool([True, False], name="flags")
# Conditional nesting: only expose dropout for Adam optimizer
def dropout_cfg(hp):
return hp.float(0.5, name="dropout", min=0.0, max=1.0)
if optimizer == "adam":
hp.nest(dropout_cfg, name="dropout_cfg")
return {
"batch_size": batch_size,
"lr": lr,
"optimizer": optimizer,
"flags": flags,
}
This example demonstrates type declaration, bounds enforcement, HPO metadata injection, and conditional parameter exposure through nesting.
Instantiating Configurations with Concrete Values
To materialize a configuration with specific values, use the instantiate function from src/hypster/core.py:
from hypster import instantiate
from my_config import model_cfg
values = {
"batch_size": 64,
"learning_rate": 0.01,
"optimizer": "sgd",
"flags": [True, True],
}
cfg = instantiate(model_cfg, values=values)
print(cfg)
# Output: {'batch_size': 64, 'lr': 0.01, 'optimizer': 'sgd', 'flags': [True, True]}
The instantiate call validates the supplied dictionary against your declarations, raises ValueError for missing or invalid keys, and returns the configuration dictionary produced by your function.
Integrating with Optuna for Hyperparameter Optimization
To sample from the complete search space defined in your configuration, use the Optuna integration:
import optuna
from hypster.hpo.optuna import suggest_values
from my_config import model_cfg
def objective(trial):
# Sample values according to HPO specifications
sampled = suggest_values(trial, config=model_cfg)
# Build and evaluate your model using sampled parameters
# Returns a scalar loss for minimization
return (sampled["lr"] - 0.01) ** 2 + (sampled["batch_size"] - 64) ** 2
study = optuna.create_study(direction="minimize")
study.optimize(objective, n_trials=30)
print("Best hyperparameters:", study.best_params)
The suggest_values function (lines 39-51 in src/hypster/hpo/optuna.py) uses the _HPProxy to translate your HP calls into Optuna's trial.suggest_* methods, automatically respecting the HpoInt, HpoFloat, and HpoCategorical specifications.
Error Handling and Validation
Hypster provides robust error handling through the validation layers in hp_calls.py. When instantiate detects parameters supplied in the values dictionary that were never accessed during configuration execution, it invokes _handle_unknown_parameters with configurable behavior:
- Strict mode: Raises an error for any unknown key
- Warning mode: Logs suggestions for similar valid names using
suggest_similar_names - Ignore mode: Silently drops unused parameters
This prevents silent failures from typos in parameter names and ensures configuration integrity across refactoring.
Summary
- Define parameter spaces using Python functions that accept an
HPobject and call methods likeint(),float(),select(), andmulti_bool(). - Enforce constraints through the
min,max,strict, andnameparameters, validated by classes insrc/hypster/hp_calls.py. - Enable optimization by passing
hpo_specobjects (HpoInt,HpoFloat,HpoCategorical) to integrate with Optuna viasrc/hypster/hpo/optuna.py. - Materialize configurations using
instantiate()fromsrc/hypster/core.py, which validates inputs and handles unknown parameter detection. - Nest configurations using
hp.nest()to create modular, hierarchical spaces with automatic namespace management.
Frequently Asked Questions
How do I create conditional parameters that only appear under certain values?
Use the nest method to conditionally expose parameter sub-spaces. Define a child configuration function and wrap it in an if statement based on the parent parameter value. For example, only expose dropout_rate when optimizer == "adam" by nesting the dropout configuration inside a conditional block. The HP class tracks namespace extensions automatically, ensuring child parameters are properly scoped.
What validation errors can Hypster catch during instantiation?
Hypster validates parameter types, bounds (min/max constraints), categorical choices, and required names through the validator classes in src/hypster/hpo/hp_calls.py. The instantiate function in src/hypster/core.py also detects unknown parameters—values supplied but never accessed—and can suggest similar valid names using fuzzy matching via suggest_similar_names.
How do I specify logarithmic scaling for hyperparameter optimization?
Pass an HpoInt or HpoFloat instance to the hpo_spec= argument of the corresponding HP method. Set scale="log" to enable logarithmic scaling, or use distribution="loguniform" for HpoFloat to sample from a log-uniform distribution. These specifications are interpreted by the _HPProxy class when using Optuna integration.
Can I define multi-valued parameters like lists of integers or booleans?
Yes. Use the multi_int, multi_float, multi_text, or multi_bool methods on the HP object. These create MultiValueSpec instances validated by MultiValidator, allowing you to specify lists of values with optional element-wise constraints and defaults.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →