feat: make code conform to pylint

This commit is contained in:
Krzysztof Rudnicki 2023-04-17 22:49:40 +02:00
parent 0271bfb1eb
commit 8cc2316ce7
2 changed files with 156 additions and 111 deletions

3
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"python.linting.pylintArgs": ["--generate-members"]
}

View File

@ -13,119 +13,154 @@ import numpy as np
def rastrigin(x_argument, y_argument): def rastrigin(x_argument, y_argument):
""" Define the Rastrigin function """ """Define the Rastrigin function"""
return 20 + x_argument**2 - 10 * np.cos(2 * np.pi * x_argument) + \ return (
y_argument**2 - 10 * np.cos(2 * np.pi * y_argument) 20
+ x_argument**2
- 10 * np.cos(2 * np.pi * x_argument)
+ y_argument**2
- 10 * np.cos(2 * np.pi * y_argument)
)
def generate(population, def generate(
number_of_parents=5, population,
size_of_population=20, arguments
mutation_strength=0.1, ):
): """Run single generation"""
""" Run single generation """
# Evaluate the fitness of each individual # Evaluate the fitness of each individual
fitness = np.array([rastrigin(x_point_value, y_point_value) fitness = np.array(
for x_point_value, y_point_value in population]) [
rastrigin(x_point_value, y_point_value)
for x_point_value, y_point_value in population
]
)
# Select the top number_of_parents individuals # Select the top arguments["number_of_parents"] individuals
parents = population[np.argsort(fitness)[:number_of_parents]] parents = population[np.argsort(fitness)[:arguments["number_of_parents"]]]
# Generate the next generation of lambda individuals by recombination # Generate the next generation of lambda individuals by recombination
children = np.concatenate( children = np.concatenate(
[np.random.permutation(parents) for i in range((size_of_population//number_of_parents)+1)]) [
children = children[:size_of_population] np.random.permutation(parents)
for i in range((arguments["size_of_population"] // arguments["number_of_parents"]) + 1)
]
)
children = children[:arguments["size_of_population"]]
# Add mutation to the children # Add mutation to the children
mutation = np.random.normal( mutation = np.random.normal(
loc=0, scale=mutation_strength, size=( loc=0, scale=arguments["mutation_strength"], size=(arguments["size_of_population"], 2)
size_of_population, 2)) )
population = children + mutation population = children + mutation
return fitness, population return fitness, population
def evolution_strategy( def evolution_strategy(
number_of_parents=5, arguments,
size_of_population=20, no_display=False,
mutation_strength=0.1,
number_of_generations=123,
min_max=(-5.12, 5.12),
number_of_outputs = 10,
no_display = False,
save_results = False
): ):
""" Define the Evolutionary Strategy (μ, λ) algorithm """ """Define the Evolutionary Strategy (μ, λ) algorithm"""
# Initialize the population # Initialize the population
print_info = [] print_info = []
population = np.random.uniform( population = np.random.uniform(
low=min_max[0], high=min_max[1], size=( low=arguments["min"], high=arguments["max"], size=(arguments["size_of_population"], 2)
size_of_population, 2)) )
summary = [] summary = []
if not no_display: if not no_display:
print_info.append((population, 0, f"0:nop-{number_of_parents}:sop-{size_of_population}:ms-{mutation_strength}:nog-{number_of_generations}:min-max-{min_max}:noo-{number_of_outputs}")) print_info.append(
number_of_outputs = min([number_of_outputs-1, number_of_generations]) (population,
0,
f"""0:nop-{arguments["number_of_parents"]}:sop-{arguments["size_of_population"]}:
ms-{arguments["mutation_strength"]}:nog-{arguments["number_of_generations"]}:
min-max-{arguments["min"], arguments["max"]}:noo-{arguments["number_of_outputs"]}""",
))
arguments["number_of_outputs"] = min(
[arguments["number_of_outputs"] - 1, arguments["number_of_generations"]])
# Iterate until we reach max number of generate and terminate # Iterate until we reach max number of generate and terminate
for generation_number in range(1, number_of_generations+1): for generation_number in range(1, arguments["number_of_generations"] + 1):
fitness, population = generate( fitness, population = generate(
population, population, arguments)
number_of_parents, step = (
size_of_population, arguments["number_of_generations"] // arguments["number_of_outputs"]
mutation_strength) if arguments["number_of_generations"] % arguments["number_of_outputs"] == 0
step = number_of_generations//number_of_outputs \ else arguments["number_of_generations"] // (arguments["number_of_outputs"] - 1)
if number_of_generations % number_of_outputs == 0 \ )
else number_of_generations//(number_of_outputs-1) offset = arguments["number_of_generations"] % step
offset = number_of_generations % step
if (generation_number - offset) % step == 0 and not no_display: if (generation_number - offset) % step == 0 and not no_display:
print_info.append((population, generation_number, f"{generation_number}:nop_{number_of_parents}:sop_{size_of_population}:ms_{mutation_strength}:nog_{number_of_generations}:min_max_{min_max}:noo_{number_of_outputs}")) print_info.append(
(population,
generation_number,
f"""{generation_number}:nop_{arguments["number_of_parents"]}:
sop_{arguments["size_of_population"]}:ms_{arguments["mutation_strength"]}:
nog_{arguments["number_of_generations"]}:
min_max_{arguments["min"], arguments["max"]}:
noo_{arguments["number_of_outputs"]}""",
))
summary.append(population) summary.append(population)
# Evaluate the fitness of the final population # Evaluate the fitness of the final population
fitness = np.array([rastrigin(x_point_value, y_point_value) fitness = np.array(
for x_point_value, y_point_value in population]) [
rastrigin(x_point_value, y_point_value)
for x_point_value, y_point_value in population
]
)
# Return the best individual found # Return the best individual found
best_idx = np.argmin(fitness) best_idx = np.argmin(fitness)
return population[best_idx], fitness[best_idx], population, total_time, print_info, summary return (
population[best_idx],
fitness[best_idx],
population,
print_info,
summary,
)
def print_help(): def print_help():
""" Print program functionality and how to access it """ """Print program functionality and how to access it"""
print(""" print(
python main.py - Default functionality optimizing Rastrigin function file_ (x_point_value, y_point_value) = """
python main.py - Default functionality optimizing
Rastrigin function file_ (x_point_value, y_point_value) =
20 + (x_point_value^2 - 10cos(2πx)) + (y_point_value^2 - 10 cos(2πy)) 20 + (x_point_value^2 - 10cos(2πx)) + (y_point_value^2 - 10 cos(2πy))
using Evolutionary Strategy (μ, λ), using only default values using Evolutionary Strategy (μ, λ), using only default values
Default values: Default values:
number_of_parents=5, arguments["number_of_parents"]=5,
size_of_population=20, arguments["size_of_population"]=20,
mutation_strength=0.1, arguments["mutation_strength"]=0.1,
number_of_generations=100, arguments["number_of_generations"]=100,
min_value=-5.12, min_value=-5.12,
max_value=5.12 max_value=5.12
number_of_outputs = 100 arguments["number_of_outputs"] = 100
python main.py -h --help print this prompt python main.py -h --help print this prompt
Any of the default values an be changed using arguments: Any of the default values an be changed using arguments:
-nop --number_of_parents [number] -nop --arguments["number_of_parents"] [number]
-sop --size_of_population [number] -sop --arguments["size_of_population"] [number]
-ms --mutation_strength [number] -ms --arguments["mutation_strength"] [number]
-nog --number_of_generations [number] -nog --arguments["number_of_generations"] [number]
-min --min_value [number] -min --min_value [number]
-max --max_value [number] -max --max_value [number]
-noo, --number_of_outputs [number] -noo, --arguments["number_of_outputs"] [number]
Those arguments can be given in any order and any argument which was not entered will be replaced with default value, Those arguments can be given in any order and any argument
which was not entered will be replaced with default value,
Additional flags: Additional flags:
-nd, --no-display (does not show the plots) -nd, --no-display (does not show the plots)
-s, --save (if issued WILL save the files) -s, --save (if issued WILL save the files)
exemplary use: exemplary use:
python main.py -nop 5 -sop 20 -ms 0.1 -i 100 -min -5.12 -max 5.12 -noo 100 python main.py -nop 5 -sop 20 -ms 0.1 -i 100 -min -5.12 -max 5.12 -noo 100
""") """
)
def get_output_bounds(x_data, y_data): def get_output_bounds(x_data, y_data):
"""Get x and y output limits for pyplot""" """Get x and y output limits for pyplot"""
# min_size = 0.2 # min_size = 0.2
min_output_size = ARGUMENTS["mutation_strength"]*10 min_output_size = ARGUMENTS["mutation_strength"] * 10
xmin = min(x_data) xmin = min(x_data)
xmax = max(x_data) xmax = max(x_data)
@ -137,21 +172,25 @@ def get_output_bounds(x_data, y_data):
if min_output_size is None: if min_output_size is None:
min_output_size = max(x_diff, y_diff) min_output_size = max(x_diff, y_diff)
margin = max(x_diff, y_diff)/5 margin = max(x_diff, y_diff) / 5
if x_diff < min_output_size: if x_diff < min_output_size:
xmax += (min_output_size - x_diff)/2 xmax += (min_output_size - x_diff) / 2
xmin -= (min_output_size - x_diff)/2 xmin -= (min_output_size - x_diff) / 2
if y_diff < min_output_size: if y_diff < min_output_size:
ymax += (min_output_size - y_diff)/2 ymax += (min_output_size - y_diff) / 2
ymin -= (min_output_size - y_diff)/2 ymin -= (min_output_size - y_diff) / 2
x_bounds = [xmin-margin, xmax+margin] x_bounds = [xmin - margin, xmax + margin]
y_bounds = [ymin-margin, ymax+margin] y_bounds = [ymin - margin, ymax + margin]
return x_bounds, y_bounds return x_bounds, y_bounds
def output(population_output, generation_number, file_name = "temp", save_results = False): def output(
""" Draw result of our function """ population_output,
generation_number,
file_name="temp",
save_results=False):
"""Draw result of our function"""
# define the visualization params # define the visualization params
colors = np.random.rand(len(population_output)) colors = np.random.rand(len(population_output))
@ -193,12 +232,8 @@ def output(population_output, generation_number, file_name = "temp", save_result
os.unlink(file_.name) os.unlink(file_.name)
def print_summary(populations, file_name = "temp_summary", save_results = False): def print_summary(populations, file_name="temp_summary", save_results=False):
""" Draw result of our function for chosen generations """ """Draw result of our function for chosen generations"""
# define the visualization params
main_color = [[1, 1, 1]] * len(populations[0])
final_color = [[0, 1, 0]] * len(populations[0])
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as file_: with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as file_:
# iterate over the optimization steps # iterate over the optimization steps
@ -217,10 +252,14 @@ def print_summary(populations, file_name = "temp_summary", save_results = False)
if ind == 0: if ind == 0:
bounds = get_output_bounds(x_data, y_data) bounds = get_output_bounds(x_data, y_data)
# plot the data # plot the data
transparency = ind/(len(populations)-1) transparency = ind / (len(populations) - 1)
color = [[transparency, 0, 0]] * len(pop) color = [[transparency, 0, 0]] * len(pop)
plt.scatter(x_data, y_data, c=color, plt.scatter(
alpha=transparency, label=f"{ind}") x_data,
y_data,
c=color,
alpha=transparency,
label=f"{ind}")
plt.xlim(bounds[0]) plt.xlim(bounds[0])
plt.ylim(bounds[1]) plt.ylim(bounds[1])
plt.savefig(file_.name) plt.savefig(file_.name)
@ -231,8 +270,7 @@ def print_summary(populations, file_name = "temp_summary", save_results = False)
cv2.imwrite("SUMMARY:" + file_name + ".jpg", image) cv2.imwrite("SUMMARY:" + file_name + ".jpg", image)
# show the image, provide window name first # show the image, provide window name first
cv2.imshow(f"Summary", image) cv2.imshow("Summary", image)
# add wait key. window waits until user presses a key and quits if # add wait key. window waits until user presses a key and quits if
# the key is 'q' # the key is 'q'
@ -247,7 +285,7 @@ def print_summary(populations, file_name = "temp_summary", save_results = False)
def user_input(): def user_input():
""" Handle user terminal arguments""" """Handle user terminal arguments"""
arguments = { arguments = {
"number_of_parents": 5, "number_of_parents": 5,
"size_of_population": 20, "size_of_population": 20,
@ -257,61 +295,65 @@ def user_input():
"max": 5.12, "max": 5.12,
"number_of_outputs": 10, "number_of_outputs": 10,
"no_display": False, "no_display": False,
"save": False} "save": False,
}
for index, argument in enumerate(sys.argv): for index, argument in enumerate(sys.argv):
if argument in ('-h', '--help'): if argument in ("-h", "--help"):
print_help() print_help()
sys.exit() sys.exit()
if argument in ('-nop', '--number_of_parents'): if argument in ("-nop", "--number_of_parents"):
arguments["number_of_parents"] = int(sys.argv[index+1]) arguments["number_of_parents"] = int(sys.argv[index + 1])
if argument in ('-sop', '--size_of_population'): if argument in ("-sop", "--size_of_population"):
arguments["size_of_population"] = int(sys.argv[index+1]) arguments["size_of_population"] = int(sys.argv[index + 1])
if argument in ('-ms', '--mutation_strength'): if argument in ("-ms", "--mutation_strength"):
arguments["mutation_strength"] = float(sys.argv[index+1]) arguments["mutation_strength"] = float(sys.argv[index + 1])
if argument in ('-nog', '--number_of_generations'): if argument in ("-nog", "--number_of_generations"):
arguments["number_of_generations"] = int(sys.argv[index+1]) arguments["number_of_generations"] = int(sys.argv[index + 1])
if argument in ('-min', '--min_value'): if argument in ("-min", "--min_value"):
arguments["min"] = float(sys.argv[index+1]) arguments["min"] = float(sys.argv[index + 1])
if argument in ('-max', '--max_value'): if argument in ("-max", "--max_value"):
arguments["max"] = float(sys.argv[index+1]) arguments["max"] = float(sys.argv[index + 1])
if argument in ('-noo', '--number_of_outputs'): if argument in ("-noo", "--number_of_outputs"):
arguments["number_of_outputs"] = int(sys.argv[index + 1]) arguments["number_of_outputs"] = int(sys.argv[index + 1])
if argument in ('-nd', '--no_display'): if argument in ("-nd", "--no_display"):
arguments["no_display"] = True arguments["no_display"] = True
if argument in ('-s', '--save'): if argument in ("-s", "--save"):
arguments["save"] = True arguments["save"] = True
return arguments return arguments
def print_output(print_info, save_results, summary): def print_output(print_info, save_results, summary):
""" Prints out population and summary plots """
for population, generation_number, file_name in print_info: for population, generation_number, file_name in print_info:
output(population, generation_number, file_name, save_results) output(population, generation_number, file_name, save_results)
summary_file_name = file_name summary_file_name = file_name
print_summary(summary, summary_file_name, save_results) print_summary(summary, summary_file_name, save_results)
# Ran first in the code # Ran first in the code
if __name__ == "__main__": if __name__ == "__main__":
# Run the Evolutionary Strategy algorithm # Run the Evolutionary Strategy algorithm
ARGUMENTS = user_input() ARGUMENTS = user_input()
total_time = 0 TOTAL_TIME = 0
start_time = time.perf_counter() start_time = time.perf_counter()
best_individual, best_fitness, output_population, generation_time, print_info, summary = evolution_strategy( (
ARGUMENTS["number_of_parents"], best_individual,
ARGUMENTS["size_of_population"], best_fitness,
ARGUMENTS["mutation_strength"], output_population,
ARGUMENTS["number_of_generations"], PRINT_INFO,
(ARGUMENTS["min"], ARGUMENTS["max"]), SUMMARY,
ARGUMENTS["number_of_outputs"], ) = evolution_strategy(
ARGUMENTS,
ARGUMENTS["no_display"], ARGUMENTS["no_display"],
ARGUMENTS["save"]) )
end_time = time.perf_counter() end_time = time.perf_counter()
if not ARGUMENTS["no_display"]: if not ARGUMENTS["no_display"]:
print_output(print_info, ARGUMENTS["save"], summary) print_output(PRINT_INFO, ARGUMENTS["save"], SUMMARY)
total_time = end_time - start_time TOTAL_TIME = end_time - start_time
time_per_generation = total_time / \ time_per_generation = TOTAL_TIME / ARGUMENTS["number_of_generations"]
ARGUMENTS["number_of_generations"]
print("Best individual found:", best_individual) print("Best individual found:", best_individual)
print("Best fitness found:", best_fitness) print("Best fitness found:", best_fitness)
print("total_generation_time: ", total_time) print("total_generation_time: ", TOTAL_TIME)
print("time_per_generation: ", time_per_generation) print("time_per_generation: ", time_per_generation)