hdim-opt 1.1.3__tar.gz → 1.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {hdim_opt-1.1.3 → hdim_opt-1.2.2}/PKG-INFO +1 -1
- {hdim_opt-1.1.3 → hdim_opt-1.2.2}/hdim_opt/__init__.py +1 -1
- {hdim_opt-1.1.3 → hdim_opt-1.2.2}/hdim_opt/hyperellipsoid_sampling.py +1 -1
- {hdim_opt-1.1.3 → hdim_opt-1.2.2}/hdim_opt/quasar_optimization.py +91 -55
- {hdim_opt-1.1.3 → hdim_opt-1.2.2}/hdim_opt.egg-info/PKG-INFO +1 -1
- {hdim_opt-1.1.3 → hdim_opt-1.2.2}/pyproject.toml +1 -1
- {hdim_opt-1.1.3 → hdim_opt-1.2.2}/README.md +0 -0
- {hdim_opt-1.1.3 → hdim_opt-1.2.2}/hdim_opt/quasar_helpers.py +0 -0
- {hdim_opt-1.1.3 → hdim_opt-1.2.2}/hdim_opt/sobol_sampling.py +0 -0
- {hdim_opt-1.1.3 → hdim_opt-1.2.2}/hdim_opt/sobol_sensitivity.py +0 -0
- {hdim_opt-1.1.3 → hdim_opt-1.2.2}/hdim_opt/test_functions.py +0 -0
- {hdim_opt-1.1.3 → hdim_opt-1.2.2}/hdim_opt.egg-info/SOURCES.txt +0 -0
- {hdim_opt-1.1.3 → hdim_opt-1.2.2}/hdim_opt.egg-info/dependency_links.txt +0 -0
- {hdim_opt-1.1.3 → hdim_opt-1.2.2}/hdim_opt.egg-info/requires.txt +0 -0
- {hdim_opt-1.1.3 → hdim_opt-1.2.2}/hdim_opt.egg-info/top_level.txt +0 -0
- {hdim_opt-1.1.3 → hdim_opt-1.2.2}/setup.cfg +0 -0
|
@@ -553,7 +553,7 @@ def sample(n_samples, bounds,
|
|
|
553
553
|
# dark visualization parameters for better sample visuals
|
|
554
554
|
|
|
555
555
|
# samples
|
|
556
|
-
fig, ax = plt.subplots(1,2,figsize=(
|
|
556
|
+
fig, ax = plt.subplots(1,2,figsize=(9,5))
|
|
557
557
|
|
|
558
558
|
ax[0].scatter(hds_sequence_plot[:, 0], hds_sequence_plot[:, 1], s=0.67, zorder=5, color='deepskyblue',
|
|
559
559
|
label='HDS Samples')
|
|
@@ -58,9 +58,12 @@ def initialize_population(popsize, bounds, init, hds_weights, seed, verbose):
|
|
|
58
58
|
print(f'Initializing: Random population (N={popsize}, D={n_dimensions}).')
|
|
59
59
|
initial_population = np.random.uniform(low=bounds[:, 0], high=bounds[:, 1], size=(popsize, n_dimensions))
|
|
60
60
|
else:
|
|
61
|
-
|
|
61
|
+
if init.ndim == 1:
|
|
62
|
+
initial_population = init.reshape(-1,1)
|
|
63
|
+
else:
|
|
64
|
+
initial_population = init
|
|
62
65
|
if verbose:
|
|
63
|
-
custom_popsize, custom_n_dimensions =
|
|
66
|
+
custom_popsize, custom_n_dimensions = initial_population.shape
|
|
64
67
|
print(f'Initializing: Custom population (N={custom_popsize}, D={custom_n_dimensions}).')
|
|
65
68
|
|
|
66
69
|
return initial_population
|
|
@@ -260,14 +263,13 @@ def evolve_generation(obj_function, population, fitnesses, best_solution,
|
|
|
260
263
|
|
|
261
264
|
return new_population, new_fitnesses
|
|
262
265
|
|
|
263
|
-
def
|
|
266
|
+
def asym_reinit(population, current_fitnesses, bounds, reinit_method, seed, vectorized):
|
|
264
267
|
'''
|
|
265
268
|
Objective:
|
|
266
269
|
- Reinitializes the worst 33% solutions in the population.
|
|
267
|
-
- Locations are determined based on
|
|
268
|
-
-
|
|
269
|
-
|
|
270
|
-
- Conceptualizes particles tunneling to a more stable location.
|
|
270
|
+
- Locations are determined based on either:
|
|
271
|
+
- 'covariance' (default): Gaussian distribution from the covariance of 25% best solutions (exploitation).
|
|
272
|
+
- 'sobol': Uniformly Sobol distributed within the bounds (exploration).
|
|
271
273
|
'''
|
|
272
274
|
|
|
273
275
|
# reshape depending on vectorized input
|
|
@@ -276,60 +278,90 @@ def covariance_reinit(population, current_fitnesses, bounds, vectorized):
|
|
|
276
278
|
else:
|
|
277
279
|
popsize, dimensions = population.shape
|
|
278
280
|
|
|
279
|
-
# handle case where not enough points for covariance matrix
|
|
281
|
+
# handle case where not enough points for reliable covariance matrix
|
|
280
282
|
if popsize < dimensions + 1:
|
|
281
283
|
return population
|
|
282
|
-
|
|
283
|
-
# keep 25% of best solutions
|
|
284
|
-
num_to_keep_factor = 0.25
|
|
285
|
-
num_to_keep = int(popsize * num_to_keep_factor)
|
|
286
|
-
if num_to_keep <= dimensions:
|
|
287
|
-
num_to_keep = dimensions + 1 # minimum sample size scaled by dimensions
|
|
288
|
-
|
|
289
|
-
# identify best solutions to calculate covariance gaussian model
|
|
290
|
-
sorted_indices = np.argsort(current_fitnesses)
|
|
291
|
-
best_indices = sorted_indices[:num_to_keep]
|
|
292
|
-
if vectorized:
|
|
293
|
-
best_solutions = population[:, best_indices]
|
|
294
|
-
else:
|
|
295
|
-
best_solutions = population[best_indices]
|
|
296
284
|
|
|
297
|
-
# learn full-covariance matrix
|
|
298
|
-
if vectorized:
|
|
299
|
-
mean_vector = np.mean(best_solutions, axis=1)
|
|
300
|
-
cov_matrix = np.cov(best_solutions)
|
|
301
|
-
else:
|
|
302
|
-
mean_vector = np.mean(best_solutions, axis=0)
|
|
303
|
-
cov_matrix = np.cov(best_solutions, rowvar=False)
|
|
304
|
-
|
|
305
|
-
# add epsilon to the diagonal to prevent singular matrix issues
|
|
306
|
-
cov_matrix += np.eye(dimensions) * epsilon
|
|
307
|
-
|
|
308
285
|
# identify solutions to be reset
|
|
309
286
|
reset_population = 0.33
|
|
310
287
|
num_to_replace = int(popsize * reset_population)
|
|
288
|
+
if num_to_replace == 0:
|
|
289
|
+
return population
|
|
290
|
+
|
|
291
|
+
sorted_indices = np.argsort(current_fitnesses)
|
|
311
292
|
worst_indices = sorted_indices[-num_to_replace:]
|
|
293
|
+
|
|
294
|
+
# initializing new solutions
|
|
295
|
+
new_solutions = None
|
|
296
|
+
|
|
297
|
+
# covariance reinitalization; exploitation
|
|
298
|
+
if reinit_method == 'covariance':
|
|
299
|
+
|
|
300
|
+
# keep 25% of best solutions
|
|
301
|
+
num_to_keep_factor = 0.25
|
|
302
|
+
num_to_keep = int(popsize * num_to_keep_factor)
|
|
303
|
+
if num_to_keep <= dimensions:
|
|
304
|
+
num_to_keep = dimensions + 1 # minimum sample size scaled by dimensions
|
|
305
|
+
|
|
306
|
+
# identify best solutions to calculate covariance gaussian model
|
|
307
|
+
best_indices = sorted_indices[:num_to_keep]
|
|
308
|
+
if vectorized:
|
|
309
|
+
best_solutions = population[:, best_indices]
|
|
310
|
+
else:
|
|
311
|
+
best_solutions = population[best_indices]
|
|
312
|
+
|
|
313
|
+
# learn full-covariance matrix
|
|
314
|
+
if vectorized:
|
|
315
|
+
mean_vector = np.mean(best_solutions, axis=1)
|
|
316
|
+
cov_matrix = np.cov(best_solutions)
|
|
317
|
+
else:
|
|
318
|
+
mean_vector = np.mean(best_solutions, axis=0)
|
|
319
|
+
cov_matrix = np.cov(best_solutions, rowvar=False)
|
|
312
320
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
321
|
+
# add epsilon to the diagonal to prevent singular matrix issues
|
|
322
|
+
cov_matrix += np.eye(dimensions) * epsilon
|
|
323
|
+
|
|
324
|
+
# new solutions sampled from multivariate normal distribution
|
|
317
325
|
new_solutions_sampled = np.random.multivariate_normal(mean=mean_vector, cov=cov_matrix, size=num_to_replace)
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
if vectorized:
|
|
322
|
-
noise = np.random.normal(0, noise_scale[:, np.newaxis], size=new_solutions_sampled.shape)
|
|
323
|
-
new_solutions = new_solutions_sampled + noise
|
|
324
|
-
else:
|
|
325
|
-
noise = np.random.normal(0, noise_scale, size=new_solutions_sampled.shape)
|
|
326
|
-
new_solutions = new_solutions_sampled + noise
|
|
326
|
+
|
|
327
|
+
# add noise for exploration
|
|
328
|
+
noise_scale = (bounds[:, 1] - bounds[:, 0]) / 20.0
|
|
327
329
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
330
|
+
# reshape
|
|
331
|
+
if vectorized:
|
|
332
|
+
new_solutions_sampled = new_solutions_sampled.T
|
|
333
|
+
noise = np.random.normal(0, noise_scale[:, np.newaxis], size=new_solutions_sampled.shape)
|
|
334
|
+
new_solutions = new_solutions_sampled + noise
|
|
335
|
+
else:
|
|
336
|
+
noise = np.random.normal(0, noise_scale, size=new_solutions_sampled.shape)
|
|
337
|
+
new_solutions = new_solutions_sampled + noise
|
|
338
|
+
|
|
339
|
+
# sobol reinitialization (high exploration)
|
|
340
|
+
elif reinit_method == 'sobol':
|
|
341
|
+
|
|
342
|
+
# generate sobol samples
|
|
343
|
+
sobol_sampler = stats.qmc.Sobol(d=dimensions, seed=seed)
|
|
344
|
+
sobol_samples_unit = sobol_sampler.random(n=num_to_replace)
|
|
345
|
+
|
|
346
|
+
bounds_low = bounds[:, 0]
|
|
347
|
+
bounds_high = bounds[:, 1]
|
|
348
|
+
scaled_samples = stats.qmc.scale(sobol_samples_unit, bounds_low, bounds_high)
|
|
349
|
+
|
|
350
|
+
# reshape
|
|
351
|
+
if vectorized:
|
|
352
|
+
new_solutions = scaled_samples.T
|
|
353
|
+
else:
|
|
354
|
+
new_solutions = scaled_samples
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
# update the selected worst indices population
|
|
358
|
+
if new_solutions is not None:
|
|
359
|
+
if vectorized:
|
|
360
|
+
population[:, worst_indices] = np.clip(new_solutions,
|
|
361
|
+
bounds[:, np.newaxis, 0],
|
|
362
|
+
bounds[:, np.newaxis, 1])
|
|
363
|
+
else:
|
|
364
|
+
population[worst_indices] = np.clip(new_solutions, bounds[:, 0], bounds[:, 1])
|
|
333
365
|
|
|
334
366
|
return population
|
|
335
367
|
|
|
@@ -342,7 +374,7 @@ def optimize(func, bounds, args=(),
|
|
|
342
374
|
patience=np.inf, vectorized=False,
|
|
343
375
|
hds_weights=None, kwargs={},
|
|
344
376
|
constraints=None, constraint_penalty=1e9,
|
|
345
|
-
reinitialization=True,
|
|
377
|
+
reinitialization=True, reinitialization_method='covariance',
|
|
346
378
|
verbose=True, plot_solutions=True, num_to_plot=10, plot_contour=True,
|
|
347
379
|
workers=1, seed=None
|
|
348
380
|
):
|
|
@@ -351,7 +383,7 @@ def optimize(func, bounds, args=(),
|
|
|
351
383
|
- Finds the optimal solution for a given objective function.
|
|
352
384
|
- Designed for non-differentiable, high-dimensional problems.
|
|
353
385
|
- Test functions available for local testing, called as hdim_opt.test_functions.function_name.
|
|
354
|
-
- Existing test functions: [rastrigin, ackley, sinusoid, sphere]
|
|
386
|
+
- Existing test functions: [rastrigin, ackley, sinusoid, sphere, shubert].
|
|
355
387
|
|
|
356
388
|
Inputs:
|
|
357
389
|
- func: Objective function to minimize.
|
|
@@ -400,9 +432,13 @@ def optimize(func, bounds, args=(),
|
|
|
400
432
|
}
|
|
401
433
|
- constraint_penalty: Penalty applied to each constraint violated, defaults to 1e12.
|
|
402
434
|
|
|
403
|
-
-
|
|
435
|
+
- reinitialization: Boolean to disable covariance reinitialization if needed.
|
|
404
436
|
- For cases where the population size is computationally prohibitive.
|
|
405
437
|
- Disabled by default for 1D problems.
|
|
438
|
+
- reinitialization_method: Type of re-sampling to use in the asymptotic reinitialization.
|
|
439
|
+
- Options are ['covariance', 'sobol'].
|
|
440
|
+
- 'covariance' (exploitative) is default for most problems.
|
|
441
|
+
- 'sobol' (explorative) is optional, for high exploration and faster computation.
|
|
406
442
|
|
|
407
443
|
- verbose: Displays prints and plots.
|
|
408
444
|
- Mutation factor distribution shown with hdim_opt.test_functions.plot_mutations()
|
|
@@ -612,7 +648,7 @@ def optimize(func, bounds, args=(),
|
|
|
612
648
|
else:
|
|
613
649
|
reinit_proba = 0.0
|
|
614
650
|
if np.random.rand() < reinit_proba:
|
|
615
|
-
population =
|
|
651
|
+
population = asym_reinit(population, current_fitnesses, bounds, reinitialization_method, seed, vectorized=vectorized)
|
|
616
652
|
|
|
617
653
|
# clip population to bounds
|
|
618
654
|
if vectorized:
|
|
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
|
|
|
6
6
|
|
|
7
7
|
[project]
|
|
8
8
|
name = "hdim_opt"
|
|
9
|
-
version = "1.
|
|
9
|
+
version = "1.2.2" # match __version__ in __init__.py
|
|
10
10
|
description = "Optimization toolkit for high-dimensional, non-differentiable problems."
|
|
11
11
|
readme = {file = "README.md", content-type = "text/markdown"}
|
|
12
12
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|