These methods handle the creation and validation of the initial design of experiments.
3.3.1 2.1 get_initial_design()
Purpose: Generate or process initial design points Used by: optimize() method at the start Calls: _generate_initial_design() if X0 is None
This method handles three scenarios: 1. Generate LHS design when X0=None 2. Include starting point x0 if provided 3. Transform user-provided X0
# Example 1: Generate default LHS designopt = SpotOptim( fun=sphere, bounds=[(-5, 5), (-5, 5)], n_initial=10, seed=42)X0 = opt.get_initial_design()print(f"Generated LHS design shape: {X0.shape}")print(f"First 3 points:\n{X0[:3]}")# Example 2: With starting point x0opt_x0 = SpotOptim( fun=sphere, bounds=[(-5, 5), (-5, 5)], n_initial=10, x0=[0.0, 0.0], seed=42)X0_with_x0 = opt_x0.get_initial_design()print(f"\nDesign with x0, first point: {X0_with_x0[0]}")# Example 3: Provide custom initial designX0_custom = np.array([[0, 0], [1, 1], [2, 2]])X0_processed = opt.get_initial_design(X0_custom)print(f"\nCustom design shape: {X0_processed.shape}")
Generated LHS design shape: (10, 2)
First 3 points:
[[-2.77395605 1.56112156]
[ 4.14140208 -1.69736803]
[-4.09417735 3.02437765]]
Design with x0, first point: [0. 0.]
Custom design shape: (3, 2)
3.3.2 2.2 _curate_initial_design()
Purpose: Remove duplicates and ensure sufficient unique points Used by: optimize() after get_initial_design() Handles: Duplicate removal, point generation, repetition for noisy functions
Original points: 5
After removing duplicates: 10
Original points: 5
After repeating (3x): 15
3.3.3 2.3 _rm_NA_values()
Purpose: Remove NaN/inf values from initial design evaluations Used by: optimize() after evaluating initial design Returns: Cleaned arrays and original count
Purpose: Validate sufficient points for surrogate fitting Used by: optimize() after handling NaN values Raises: ValueError if insufficient points
opt = SpotOptim( fun=sphere, bounds=[(-5, 5), (-5, 5)], n_initial=10, seed=42)# Example 1: Sufficient points - no errory0_sufficient = np.array([1.0, 2.0, 3.0, 4.0, 5.0])try: opt._check_size_initial_design(y0_sufficient, n_evaluated=10)print("✓ Sufficient points - validation passed")exceptValueErroras e:print(f"✗ Error: {e}")# Example 2: Insufficient points - raises errory0_insufficient = np.array([1.0]) # Only 1 point, need at least 3 for 2Dtry: opt._check_size_initial_design(y0_insufficient, n_evaluated=10)print("✓ Validation passed")exceptValueErroras e:print(f"✗ Expected error: {e}")
✓ Sufficient points - validation passed
✗ Expected error: Insufficient valid initial design points: only 1 finite value(s) out of 10 evaluated. Need at least 3 points to fit surrogate model. Please check your objective function or increase n_initial.
3.3.5 2.5 _get_best_xy_initial_design()
Purpose: Determine and store the best point from initial design Used by: optimize() after initial design evaluation Updates: self.best_x_ and self.best_y_ attributes
opt = SpotOptim( fun=sphere, bounds=[(-5, 5), (-5, 5)], n_initial=5, verbose=True, seed=42)# Simulate initial design (normally done in optimize())opt.X_ = np.array([[1, 2], [0, 0], [2, 1]])opt.y_ = np.array([5.0, 0.0, 5.0])opt._get_best_xy_initial_design()print(f"\nBest x from initial design: {opt.best_x_}")print(f"Best y from initial design: {opt.best_y_}")
TensorBoard logging disabled
Initial best: f(x) = 0.000000
Best x from initial design: [0 0]
Best y from initial design: 0.0
3.4 3. Surrogate Model Methods
These methods handle surrogate model operations during the optimization loop.
3.4.1 3.1 _fit_surrogate()
Purpose: Fit surrogate model to data Used by: optimize() in main loop Calls: _selection_dispatcher() if max_surrogate_points exceeded
opt = SpotOptim( fun=sphere, bounds=[(-5, 5), (-5, 5)], max_surrogate_points=10, seed=42)# Generate some training dataX = np.random.rand(50, 2) *10-5# 50 points in [-5, 5]y = np.sum(X**2, axis=1)# Fit surrogate (will select 10 best points)opt._fit_surrogate(X, y)print(f"Surrogate fitted successfully!")print(f"Surrogate model: {type(opt.surrogate).__name__}")
Purpose: Predict with uncertainty estimates, handling surrogates without return_std Used by: _acquisition_function() and plot_surrogate() Returns: Predictions and standard deviations
Purpose: Compute acquisition function value Used by: suggest_next_infill_point() for optimization Calls: _predict_with_uncertainty() Supports: Expected Improvement (EI), Probability of Improvement (PI), Mean prediction
opt = SpotOptim( fun=sphere, bounds=[(-5, 5), (-5, 5)], surrogate=GaussianProcessRegressor(), acquisition='ei', seed=42)# SetupX_train = np.array([[0, 0], [1, 1], [2, 2]])y_train = np.array([0, 2, 8])opt._fit_surrogate(X_train, y_train)opt.y_ = y_train # Needed for acquisition function# Evaluate acquisition functionx_eval = np.array([1.5, 1.5])acq_value = opt._acquisition_function(x_eval)print(f"Point to evaluate: {x_eval}")print(f"Acquisition function value (EI): {acq_value:.6f}")print(f"(Lower is better for minimization)")
Point to evaluate: [1.5 1.5]
Acquisition function value (EI): -0.000000
(Lower is better for minimization)
3.4.4 3.4 suggest_next_infill_point()
Purpose: Suggest next point to evaluate using acquisition function optimization Used by: optimize() in main loop Calls: _acquisition_function(), _handle_acquisition_failure() if needed Handles: Integer/factor rounding, duplicate avoidance
opt = SpotOptim( fun=sphere, bounds=[(-5, 5), (-5, 5)], surrogate=GaussianProcessRegressor(), acquisition='ei', seed=42)# SetupX_train = np.array([[0, 0], [1, 1], [2, 2]])y_train = np.array([0, 2, 8])opt._fit_surrogate(X_train, y_train)opt.X_ = X_trainopt.y_ = y_train# Suggest next pointx_next = opt.suggest_next_infill_point()print(f"Next point to evaluate: {x_next}")print(f"Expected to be between known points or in unexplored regions")
Next point to evaluate: [[-1.6288683 1.99062025]]
Expected to be between known points or in unexplored regions
Purpose: Apply OCBA for noisy functions to determine which points to re-evaluate Used by: optimize() in main loop when noise=True and ocba_delta > 0 Returns: Points to re-evaluate or None
Purpose: Replace NaN and infinite values with penalty plus random noise Used by: _handle_NA_new_points() and indirectly by optimize() Algorithm: penalty = max(finite_y) + 3 * std(finite_y) + noise
Original X shape: (3, 2)
Clean X shape: (1, 2)
Clean X:
[[1 2]]
Clean y: [1.]
3.6.3 5.3 _handle_NA_new_points()
Purpose: Handle NaN/inf values in new evaluation points during main loop Used by: optimize() after evaluating new points Calls: _apply_penalty_NA() and _remove_nan() Returns: None, None if all evaluations invalid (skip iteration)
TensorBoard logging disabled
Case 1: Some valid evaluations
Warning: Removed 1 sample(s) with NaN/inf values
Valid points remaining: 2
Case 2: All invalid evaluations
Warning: All objective function values are NaN or inf. Returning empty arrays.
Warning: All new evaluations were NaN/inf, skipping iteration 1
Result: None (skip iteration)
3.7 6. Main Loop Update Methods
3.7.1 6.1 _update_best_main_loop()
Purpose: Update best solution found during main optimization loop Used by: optimize() after each iteration Updates: self.best_x_ and self.best_y_ if improvement found
opt = SpotOptim( fun=sphere, bounds=[(-5, 5), (-5, 5)], n_initial=5, verbose=True, seed=42)# Simulate optimization stateopt.n_iter_ =1opt.best_x_ = np.array([1.0, 1.0])opt.best_y_ =2.0# Case 1: New best foundprint("Case 1: New best found")x_new = np.array([[0.1, 0.1], [0.5, 0.5]])y_new = np.array([0.02, 0.5])opt._update_best_main_loop(x_new, y_new)print(f"Updated best_y: {opt.best_y_}\n")# Case 2: No improvementprint("Case 2: No improvement")opt.n_iter_ =2x_no_improve = np.array([[1.5, 1.5]])y_no_improve = np.array([4.5])opt._update_best_main_loop(x_no_improve, y_no_improve)print(f"Best_y unchanged: {opt.best_y_}")
TensorBoard logging disabled
Case 1: New best found
Iter 1 | Best: 0.020000 | Rate: 0.00 | Evals: 0.0%
Updated best_y: 0.02
Case 2: No improvement
Iter 2 | Best: 0.020000 | Curr: 4.500000 | Rate: 0.00 | Evals: 0.0%
Best_y unchanged: 0.02
3.8 7. Termination Method
3.8.1 7.1 _determine_termination()
Purpose: Determine termination reason for optimization Used by: optimize() at the end Checks: Max iterations, time limit, or successful completion
opt = SpotOptim( fun=sphere, bounds=[(-5, 5), (-5, 5)], max_iter=20, max_time=10.0, seed=42)# Case 1: Maximum evaluations reachedprint("Case 1: Maximum evaluations reached")opt.y_ = np.zeros(20)start_time = time.time()msg = opt._determine_termination(start_time)print(f"Message: {msg}\n")# Case 2: Time limit exceeded (simulated)print("Case 2: Time limit exceeded")opt.y_ = np.zeros(10)start_time = time.time() -700# 11.67 minutes agomsg = opt._determine_termination(start_time)print(f"Message: {msg}\n")# Case 3: Successful completionprint("Case 3: Successful completion")opt.y_ = np.zeros(10)start_time = time.time()msg = opt._determine_termination(start_time)print(f"Message: {msg}")
Case 1: Maximum evaluations reached
Message: Optimization terminated: maximum evaluations (20) reached
Case 2: Time limit exceeded
Message: Optimization terminated: time limit (10.00 min) reached
Case 3: Successful completion
Message: Optimization finished successfully
3.9 8. Utility Methods
3.9.1 8.1 select_new()
Purpose: Select rows from A that are not in X (avoid duplicate evaluations) Used by: suggest_next_infill_point() to ensure new points are different from evaluated points
Original X:
[[1.2 2.5]
[3.7 4.1]
[5.9 6.8]]
Repaired X (first column rounded to int):
[[1. 2.5]
[4. 4.1]
[6. 6.8]]
3.9.3 8.3 _map_to_factor_values()
Purpose: Map internal integer values to original factor strings Used by: optimize() when preparing results for user Handles: Factor (categorical) variables
# Function that sometimes returns NaNdef sometimes_nan(X): X = np.atleast_2d(X) y = np.sum(X**2, axis=1)# Return NaN for large values (penalty will be applied) y[y >100] = np.nanreturn yopt = SpotOptim( fun=sometimes_nan, bounds=[(-10, 10), (-10, 10)], max_iter=20, n_initial=10, seed=42, verbose=True)result = opt.optimize()print(f"\nBest point: {result.x}")print(f"Best value: {result.fun:.6f}")print(f"Optimization succeeded despite NaN values: {result.success}")
TensorBoard logging disabled
Warning: 1 initial design point(s) returned NaN/inf and will be ignored (reduced from 10 to 9 points)
Note: Initial design size (9) is smaller than requested (10) due to NaN/inf values
Initial best: f(x) = 9.683226
Iter 1 | Best: 8.496828 | Rate: 1.00 | Evals: 50.0%
Iter 2 | Best: 4.452331 | Rate: 1.00 | Evals: 55.0%
Iter 3 | Best: 0.175858 | Rate: 1.00 | Evals: 60.0%
Iter 4 | Best: 0.033204 | Rate: 1.00 | Evals: 65.0%
Iter 5 | Best: 0.000265 | Rate: 1.00 | Evals: 70.0%
Iter 6 | Best: 0.000265 | Curr: 0.000388 | Rate: 0.83 | Evals: 75.0%
Iter 7 | Best: 0.000083 | Rate: 0.86 | Evals: 80.0%
Iter 8 | Best: 0.000005 | Rate: 0.88 | Evals: 85.0%
Iter 9 | Best: 0.000002 | Rate: 0.89 | Evals: 90.0%
Iter 10 | Best: 0.000001 | Rate: 0.90 | Evals: 95.0%
Iter 11 | Best: 0.000001 | Curr: 0.000001 | Rate: 0.82 | Evals: 100.0%
Best point: [-0.00070328 -0.00046471]
Best value: 0.000001
Optimization succeeded despite NaN values: True
3.11 10. Method Relationship Diagram
Here’s how all the methods relate to each other in the optimization workflow: