Advanced topics
Custom objective functions
Complex multi-objective optimization
Hill Climber supports multi-column data. Your objective function should accept as many arguments as you have columns. Combine multiple objectives with different weights:
def multi_objective(w, x, y, z):
"""Optimize multiple properties simultaneously for 4-column data."""
# Calculate individual objectives
mean_similarity = calculate_mean_penalty(w, x, y, z)
std_similarity = calculate_std_penalty(w, x, y, z)
structural_diversity = calculate_ks_statistics(w, x, y, z)
# Combine with weights
objective = (
10.0 * structural_diversity -
5.0 * mean_similarity -
5.0 * std_similarity
)
metrics = {
'Mean Similarity': mean_similarity,
'Std Similarity': std_similarity,
'Structural Diversity': structural_diversity
}
return metrics, objective
Handling constraints
Implement hard constraints through penalties:
def constrained_objective(x, y):
"""Optimize with constraints."""
# Calculate main objective
correlation = pearsonr(x, y)[0]
# Check constraints
penalty = 0.0
# Constraint: mean must be near 0.5
mean_x = np.mean(x)
if abs(mean_x - 0.5) > 0.1:
penalty += 100 * abs(mean_x - 0.5)
# Constraint: std must be > 0.2
std_x = np.std(x)
if std_x < 0.2:
penalty += 100 * (0.2 - std_x)
objective = correlation - penalty
return {'Correlation': correlation, 'Penalty': penalty}, objective
Replica exchange tuning
Temperature ladder configuration
Choose the appropriate temperature range and spacing:
# Wide temperature range for difficult landscapes
climber = HillClimber(
data=data,
objective_func=my_objective,
n_replicas=8,
T_min=0.0001, # Coldest replica
T_max=100.0, # Hottest replica
temperature_scheme='geometric' # Recommended for better mixing
)
# Narrow range for fine-tuning
climber = HillClimber(
data=data,
objective_func=my_objective,
n_replicas=4,
T_min=1.0,
T_max=5.0,
temperature_scheme='linear'
)
Exchange strategy selection
Different strategies for replica pairing:
even_odd (default): Alternates between even and odd pairs (0-1, 2-3, then 1-2, 3-4). Good balance of mixing and efficiency.
random: Random pair selection each round. More stochastic exploration.
all_neighbors: All neighboring pairs attempt exchange each round. More thorough but slower.
climber = HillClimber(
data=data,
objective_func=my_objective,
exchange_strategy='random', # or 'even_odd', 'all_neighbors'
exchange_interval=100 # Exchange attempts every 100 steps
)
Choosing number of replicas
4 replicas: Good default for most problems
8-12 replicas: Better exploration of complex landscapes
16+ replicas: For very difficult optimization problems
Memory consideration: Each replica maintains a copy of your data
Trade-offs: - More replicas = better exploration but more memory usage - Fewer replicas = faster per-iteration but may miss global optima
Checkpointing
For long optimizations, save intermediate progress:
climber = HillClimber(
data=data,
objective_func=my_objective,
max_time=60,
checkpoint_file='long_run.pkl' # Auto-saves after each batch
)
result = climber.climb()
Resume from a checkpoint:
# Continue with saved temperatures (default)
resumed = HillClimber.load_checkpoint(
filepath='optimization.pkl',
objective_func=my_objective
)
# Or reset temperatures to restart cooling schedule
resumed = HillClimber.load_checkpoint(
filepath='optimization.pkl',
objective_func=my_objective,
reset_temperatures=True # Restart from hot temperatures
)
# Continue from where it left off
best_data = resumed.climb()
Temperature reset on resume
By default, load_checkpoint preserves the cooled temperatures from the saved state,
allowing the optimization to continue its cooling schedule. However, you can reset
temperatures to their original ladder values:
# Reset temperatures when resuming
resumed = HillClimber.load_checkpoint(
filepath='optimization.pkl',
objective_func=my_objective,
reset_temperatures=True
)
When to reset temperatures
Escaped local minimum: If the optimization found a good solution but you want to explore more aggressively
Multiple restart strategy: Run multiple sessions with fresh temperatures for better exploration
Stuck optimization: Replicas have cooled too much and accept very few moves
When to keep saved temperatures (default)
Continuing long optimization: Natural continuation of the cooling schedule
Refining solution: Cooler temperatures help fine-tune the current best solution
Limited time remaining: Use remaining time efficiently without re-exploring
Note that resetting temperatures restarts the cooling schedule but preserves all other state including current configurations, best solutions, and optimization history.
Performance optimization
Perturbation strategies
Perturbation distribution
Perturbations are sampled from a normal distribution N(0, σ) where σ is calculated
per-feature as initial_step_spread * feature_range:
Mean is always 0 (symmetric perturbations around current values)
initial_step_spread: Fraction of each feature’s range (default: 0.25 = 25%)Each feature uses its own range for appropriate perturbations across different scales
Actual standard deviation scales automatically with your data
Time-based cooling
Optionally specify final_step_spread to linearly decrease perturbation size over time:
Step spread interpolates from
initial_step_spreadtofinal_step_spreadCooling is time-based (over
max_time), not iteration-basedEnables refined optimization near the end of long runs
Example:
climber = HillClimber(
data=data,
objective_func=my_objective,
perturb_fraction=0.001, # perturb 0.1% of elements (default)
initial_step_spread=0.25, # Start at 25% of each feature's range
final_step_spread=0.01 # End at 1% for refined optimization
)
Faster convergence
For quick convergence, use aggressive parameters:
Large initial_step_spread (0.5-1.0): Allow bigger perturbations (50-100% of range)
High perturb_fraction (0.01-0.1): Modify more points
Low T_min (0.01-0.1): More greedy optimization
Higher cooling_rate (1e-6): Faster temperature reduction
Better exploration
For thorough exploration of solution space:
Small initial_step_spread (0.05-0.1): Precise adjustments (5-10% of range)
Small final_step_spread (0.001-0.01): Very refined final optimization (0.1-1% of range)
Low perturb_fraction (0.0001-0.001): Subtle changes
High T_min (1.0-10.0): Accept more suboptimal moves
Lower cooling_rate (1e-9 to 1e-10): Gradual convergence
Algorithm visualization
The hill climbing process can be visualized as searching a fitness landscape. The algorithm:
Starts from initial data
Makes random perturbations sampled from N(0, σ) where σ =
initial_step_spread * feature_rangefor each featureEvaluates fitness via objective function
Accepts improvements (always) or worsening moves (with probability based on temperature)
Gradually reduces temperature to focus on local optimization
Optionally reduces step spread over time for refined final optimization
Returns the best solution found
Troubleshooting
No progress after many steps
Symptoms: Objective value not improving, same metrics every iteration
Solutions:
Increase
initial_step_spreadfor larger perturbations (try 0.5-1.0)Increase
perturb_fractionto modify more pointsDecrease
T_minfor more greedy optimizationCheck if objective function has bugs or is too constrained
Converging to local optima
Symptoms: Different runs find similar suboptimal solutions, exchange acceptance rate is very low
Solutions:
Increase
T_maxfor hotter replicas to explore more broadlyIncrease
n_replicasfor better temperature coverageUse smaller
cooling_rate(slower cooling) to explore longerAdjust
exchange_interval(try smaller values for more frequent exchanges)Check temperature ladder - ensure good spacing between replicas
Oscillating objective values
Symptoms: Objective improves then worsens repeatedly
Solutions:
Decrease
initial_step_spreadfor finer control (try 0.05-0.1)Use
final_step_spreadto gradually reduce perturbation size (try 0.001-0.01)Decrease
T_minto be more selectiveCheck for bugs in objective function
Ensure objective weights are balanced
Package information
Version information
To check the installed version:
import hill_climber
print(hill_climber.__version__)
The package follows semantic versioning (MAJOR.MINOR.PATCH).
License
Hill Climber is licensed under the GNU General Public License v3.0 (GPL-3.0). You are free to use, modify, and distribute this software, but any derivative works must also be released under the GPL-3.0 license.
Citation
If you use this package in your research, please cite it appropriately. Visit the GitHub repository (opens in new tab) and click the “Cite this repository” button for properly formatted citations in APA, BibTeX, or other formats.