Skip to content

Commit

Permalink
tuner fix, parallel sound, minor fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
MangaBoba committed Nov 23, 2023
1 parent 24d53fa commit 7bf80fc
Show file tree
Hide file tree
Showing 11 changed files with 137 additions and 75 deletions.
73 changes: 41 additions & 32 deletions cases/sound_waves/configuration/config_parallel.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
from copy import deepcopy
from functools import partial
from pathlib import Path

import numpy as np

from cases.sound_waves.microphone_points import Microphone
from gefest.core.geometry.datastructs.point import Point
from gefest.core.geometry.datastructs.polygon import Polygon
from gefest.tools.tuners.utils import percent_edge_variance
from gefest.tools.utils import poly_from_comsol_txt
from gefest.core.configs.optimization_params import OptimizationParams
from gefest.core.configs.tuner_params import TunerParams
Expand All @@ -23,6 +27,9 @@
MAX_PRESSURE = INITIAL_P / 2
MIN_PRESSURE = -INITIAL_P / 2

import numpy as np


import itertools
from numpy.core.umath import pi
class SoundFieldFitness(Objective):
Expand All @@ -36,7 +43,6 @@ def __init__(self, domain, estimator, best_spl, duration):
self.map_size = (round(1.2 * domain.max_y), round(1.2 * domain.max_x))
self.size_y, self.size_x = self.map_size
self.duration = duration
# obstacle_map handling

# Source position is the center of the map
self.s_y = self.size_y // 2
Expand Down Expand Up @@ -97,21 +103,22 @@ def spl(self, pressure_hist, integration_interval=60):
def _evaluate(self, ind: Structure):
self.iteration = 0
obstacle_map = np.zeros((self.size_y, self.size_x))
obstacle_map = generate_map(self.domain, ind)
pressure = np.zeros((self.size_y, self.size_x))
pressure_hist = np.zeros((self.duration, self.size_y, self.size_x))
velocities = np.zeros((self.size_y, self.size_x, 4))

for iteration in range(self.duration):
pressure_hist[iteration] = deepcopy(pressure)
velocities, pressure = self.step(velocities, pressure, obstacle_map)
# best_spl = self._reference_spl(sim)

spl = self.spl(pressure_hist)

current_spl = np.nan_to_num(spl, nan=0, neginf=0, posinf=0)
micro = Microphone(matrix=current_spl).array()
current_spl = np.concatenate(micro[1])
current_spl = np.concatenate(micro[-1])
l_f = np.sum(np.abs(deepcopy(self.best_spl) - current_spl))
return l_f
return l_f / len(current_spl)


# # # Precompute domain arguments # # #
Expand All @@ -122,48 +129,50 @@ def _evaluate(self, ind: Structure):

domain_cfg = Domain(
allowed_area=[
[0, 0],
[0, 120],
[120, 120],
[120, 0],
[0, 0],
[20, 20],
[20, 100],
[100, 100],
[100, 20],
[20, 20],
],
name='main',
min_poly_num=1,
max_poly_num=1,
max_poly_num=4,
min_points_num=3,
max_points_num=15,
polygon_side=0.0001,
min_dist_from_boundary=0.0001,
max_points_num=16,
polygon_side=0.001,
min_dist_from_boundary=0.001,
geometry_is_convex=True,
geometry_is_closed=True,
geometry='2D',
)


tuner_cfg = TunerParams(
tuner_type='optuna',
n_steps_tune=25,
hyperopt_dist='uniform',
tuner_type='sequential',
n_steps_tune=1000,
hyperopt_dist='normal',
verbose=True,
timeout_minutes=60,
variacne_generator=partial(percent_edge_variance, percent=0.5),
timeout_minutes=30,
)


best_structure = poly_from_comsol_txt(str(Path(__file__).parent) + '\\figures\\bottom_square.txt')
best_spl = SoundSimulator(domain_cfg, 50, None)(best_structure)
best_spl = SoundSimulator(domain_cfg, 200, None)(best_structure)
best_spl = np.nan_to_num(best_spl, nan=0, neginf=0, posinf=0)
micro = Microphone(matrix=best_spl).array()
best_spl = np.concatenate(micro[1])
best_spl = np.concatenate(micro[-1])

opt_params = OptimizationParams(
optimizer='gefest_ga',
domain=domain_cfg,
tuner_cfg=tuner_cfg,
n_steps=10,
pop_size=10,
n_steps=100,
pop_size=100,
postprocess_attempts=3,
mutation_prob=0.6,
crossover_prob=0.6,
mutation_prob=0.9,
crossover_prob=0.7,
mutations=[
'rotate_poly',
'resize_poly',
Expand All @@ -173,13 +182,13 @@ def _evaluate(self, ind: Structure):
'drop_poly',
'pos_change_point',
],
selector='tournament_selection',
mutation_each_prob=[0.125, 0.125, 0.15, 0.35, 0.00, 0.00, 0.25],
selector='roulette_selection',
mutation_each_prob=[0.125, 0.125, 0.25, 0.25, 0.0, 0.0, 0.25],
crossovers=[
'polygon_level',
'structure_level',
],
crossover_each_prob=[0.0, 1.0],
crossover_each_prob=[1.0, 0.0],
postprocess_rules=[
'not_out_of_bounds',
'valid_polygon_geom',
Expand All @@ -189,19 +198,19 @@ def _evaluate(self, ind: Structure):
'not_too_close_points',
],
extra=5,
estimation_n_jobs=1,
n_jobs=-1,
log_dir='logs',
run_name='run_name',
golem_keep_histoy=False,
estimation_n_jobs=-1,
n_jobs=11,
log_dir='logs/tuners_exp',
run_name='roulette_1_obj',
golem_keep_histoy=True,
golem_genetic_scheme_type='steady_state',
golem_surrogate_each_n_gen=5,
objectives=[
SoundFieldFitness(
domain_cfg,
None,
best_spl,
50
200
),
],
)
2 changes: 1 addition & 1 deletion cases/sound_waves/microphone_points.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
class Microphone:
"""Slices microphone points where makes a sound measurements."""
def __init__(self, matrix: np.ndarray = None):
self.matrix = np.random.rand(120, 120)
self.matrix = np.random.rand(120, 120) if matrix is None else matrix

def array(self):
"""Generates np.array of sound pressure."""
Expand Down
80 changes: 80 additions & 0 deletions draft_run_tuners.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import sys
from functools import partial
from pathlib import Path

from loguru import logger
from tqdm import tqdm

from gefest.core.configs.utils import load_config
from gefest.core.geometry import Point, Polygon, Structure
from gefest.core.utils.logger import LogDispatcher
from gefest.core.viz.struct_vizualizer import GIFMaker, StructVizualizer
from gefest.tools.tuners.tuner import GolemTuner
from gefest.tools.tuners.utils import percent_edge_variance

if __name__ == '__main__':

opt_params = load_config(
str(Path(__file__).parent) + '\cases\sound_waves\configuration\config_parallel.py'
)

optimized_struct = Structure(
polygons=[
Polygon(
points=[
Point(x=94.8399485030149, y=20.40034861129441),
Point(x=85.5624968428982, y=21.059993710397926),
Point(x=79.00084052541479, y=21.862700852017394),
Point(x=76.06076964736816, y=22.476934992526946),
Point(x=74.90849793039871, y=29.131517380279682),
Point(x=74.61676812657737, y=32.94679344410641),
Point(x=74.56239446772611, y=33.69226990738204),
Point(x=74.24162218587203, y=40.70530130547375),
Point(x=87.01622361737279, y=40.96813944432962),
Point(x=95.64079625726104, y=40.78198185566877),
Point(x=95.58245342099814, y=25.459142743749343),
Point(x=95.57086138193173, y=25.035926422500204),
Point(x=95.46231197117629, y=21.849202076811995),
Point(x=94.8399485030149, y=20.40034861129441),
],
fitness=[0.8770969580695486],
)
]
)

best_structure = Structure(
polygons=(
Polygon(
points=[
Point(x=95.0, y=20.0),
Point(x=75.0, y=20.0),
Point(x=75.0, y=40.0),
Point(x=95.0, y=40.0),
Point(x=95.0, y=20.0),
],
),
),
fitness=[],
)

## plot structs
# sv = StructVizualizer(opt_params.domain)
# sv.plot_structure(optimized_struct, opt_params.domain)
# sv.plot_structure(best_structure, opt_params.domain)
# from matplotlib import pyplot as plt
# plt.show(block=True)

tuner_names = ['sequential', 'simulataneous', 'iopt', 'optuna']
for tuner_name in tuner_names:
opt_params.tuner_cfg.tuner_type = tuner_name
# opt_params.tuner_cfg.variacne_generator = partial(percent_edge_variance, percent=0.5)
tuner = GolemTuner(opt_params)
tuned_individuals = tuner.tune(optimized_struct)
LogDispatcher(run_name=tuner_name).log_pop(tuned_individuals, '0')

# # MP4
# gm = GIFMaker(domain=opt_params.domain)
# for st in tqdm([best_structure, optimized_struct]):
# gm.create_frame(st, {'Tuned': st.fitness})

# gm.make_gif('Tuned individuals', 500)
9 changes: 3 additions & 6 deletions gefest/core/configs/tuner_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class TunerParams(BaseModel):
verbose: bool = True
"""GOLEM console info."""

variacne_generator: Union[Callable[[Structure], list[float]], str] = utils.average_edge_variance
variacne_generator: Union[Callable[[Structure], list[float]], str] = utils.percent_edge_variance
"""The function for generating the search space includes intervals
for each component of each point of each polygon in the provided structure.
Expand Down Expand Up @@ -80,15 +80,12 @@ def hyperopt_fun_validate(cls, value):
@classmethod
def variacne_generator_fun_validate(cls, value):
"""Checks if specified variance generation function exists."""
fun_names = ['average_edge_variance']
fun_names = ['percent_edge_variance']
if isinstance(value, str):
if value in fun_names:
return getattr(utils, value)
else:
raise ValueError(f'Invalid distribution name: {value}. Allowed names: {fun_names}')
elif isinstance(value, Callable):
if value.__module__.split('.')[0] == hp.__name__.split('.')[0]:
return value
return value
else:
raise ValueError(f'Invalid argument: {value} of type {type(value)}.')

Expand Down
2 changes: 1 addition & 1 deletion gefest/core/geometry/domain.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class Domain(BaseModel):
max_points_num: int = 50
polygon_side: float = 0.0001
min_dist_from_boundary: float = 0.0001
prohibited_area: Optional[Union[Structure, str]] = Field(default=Structure([]))
prohibited_area: Optional[Union[Structure, str]] = Field(default=Structure())
fixed_points: Optional[Union[Polygon, list[list[float]]]] = Field(default_factory=list)
geometry_is_convex: bool = True
geometry_is_closed: bool = True
Expand Down
2 changes: 1 addition & 1 deletion gefest/core/opt/operators/crossovers.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ def polygon_level_crossover(
domain: Domain,
**kwargs,
):
"""Exchanges polygons of two structure."""
"""Exchanges points of two nearest polygons in structure."""
geom = domain.geometry
s1, s2 = copy.deepcopy(s1), copy.deepcopy(s2)
intersected = False
Expand Down
1 change: 0 additions & 1 deletion gefest/core/utils/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,3 @@ def log_pop(self, pop: list[Structure], step: str):
logger.log(4, dump)

logger.remove(inividual_log_handler)
logger.info('Population logged. May be not sorted by fitness.')
5 changes: 4 additions & 1 deletion gefest/tools/optimizers/GA/GA.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@ def optimize(self) -> list[Structure]:
Returns:
list[Structure]: Optimized population.
"""
for step in tqdm(range(self.n_steps)):
pbar = tqdm(range(self.n_steps))
for step in pbar:
pbar.set_description(f'Best fitness: {self._pop[0].fitness}')
self._pop = self.selector(self._pop, self.pop_size)
child = self.crossover(self._pop)
mutated_child = self.mutation(child)
Expand All @@ -73,4 +75,5 @@ def optimize(self) -> list[Structure]:
self._pop = self.objectives_evaluator(self._pop)
self.log_dispatcher.log_pop(self._pop, str(step + 1))

pbar.set_description(f'Best fitness: {self._pop[0].fitness}')
return self._pop
3 changes: 0 additions & 3 deletions gefest/tools/samplers/standard/standard.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
from functools import partial
from typing import Callable

from loguru import logger

from gefest.core.geometry import Structure
from gefest.core.geometry.domain import Domain
from gefest.core.geometry.utils import get_random_structure
Expand Down Expand Up @@ -60,6 +58,5 @@ def sample(self, n_samples: int) -> list[Structure]:

random_pop = [ind for ind in corrected if ind is not None]

logger.info(f'{n_samples}, {len(random_pop)}')
pop = random_pop[:n_samples]
return pop
4 changes: 2 additions & 2 deletions gefest/tools/tuners/tuner.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
from gefest.core.geometry.domain import Domain
from gefest.core.opt.objective.tuner_objective import GolemObjectiveWithPreValidation
from gefest.core.opt.postproc.resolve_errors import validate
from gefest.tools.tuners.utils import VarianceGeneratorType

VarianceGeneratorType = Callable[[Structure], list[float]]
GolemTunerType = Union[IOptTuner, OptunaTuner, SequentialTuner, SimultaneousTuner]


Expand Down Expand Up @@ -124,7 +124,7 @@ def tune(
graph = self.adapter.adapt(struct)
search_space = self._generate_search_space(
graph,
self.generate_variances(struct, self.domain, self.hyperopt_distrib),
self.generate_variances(struct, self.domain),
)
tuner = self._get_tuner(graph, SearchSpace(search_space))
tuned_structures = tuner.tune(graph)
Expand Down
Loading

0 comments on commit 7bf80fc

Please sign in to comment.