-
Notifications
You must be signed in to change notification settings - Fork 13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feature/libe_gen_wrapper + Feature/asktell aposmm #209
base: main
Are you sure you want to change the base?
Changes from 20 commits
985f611
af48c3c
707b7cb
dd62c06
dc7d54a
d5a1d4b
18c1bad
1d10b02
e887f06
1b9b966
838d0d1
09bf72d
f887fca
d77f36d
3aea989
c9542d2
258c015
b4b970c
715473a
dd3e3c8
9f71994
2674dbe
1ed3c31
81a3b07
e4dc0ba
ab20e98
c797c9c
859669d
751563f
f8c3fc2
ca44a9c
ce47066
dc294e5
720bf8f
696e00e
ce8beb8
bb26486
9f2a059
52dbd2e
08a835b
bf32ec4
6232b41
595fd86
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
"""Basic example of parallel random sampling with simulations.""" | ||
|
||
from math import gamma, pi, sqrt | ||
import numpy as np | ||
from libensemble.generators import APOSMM | ||
import libensemble.gen_funcs | ||
|
||
libensemble.gen_funcs.rc.aposmm_optimizers = "nlopt" | ||
from optimas.core import VaryingParameter, Objective | ||
from libensemble.tests.regression_tests.support import ( | ||
six_hump_camel_minima as minima, | ||
) | ||
|
||
# from optimas.generators import RandomSamplingGenerator | ||
from optimas.generators import libEWrapper | ||
from optimas.evaluators import TemplateEvaluator | ||
from optimas.explorations import Exploration | ||
|
||
from multiprocessing import set_start_method | ||
|
||
set_start_method("fork", force=True) | ||
|
||
|
||
def analyze_simulation(simulation_directory, output_params): | ||
"""Analyze the simulation output. | ||
|
||
This method analyzes the output generated by the simulation to | ||
obtain the value of the optimization objective and other analyzed | ||
parameters, if specified. The value of these parameters has to be | ||
given to the `output_params` dictionary. | ||
|
||
Parameters | ||
---------- | ||
simulation_directory : str | ||
Path to the simulation folder where the output was generated. | ||
output_params : dict | ||
Dictionary where the value of the objectives and analyzed parameters | ||
will be stored. There is one entry per parameter, where the key | ||
is the name of the parameter given by the user. | ||
|
||
Returns | ||
------- | ||
dict | ||
The `output_params` dictionary with the results from the analysis. | ||
|
||
""" | ||
# Read back result from file | ||
with open("result.txt") as f: | ||
result = float(f.read()) | ||
# Fill in output parameters. | ||
output_params["f"] = result | ||
return output_params | ||
|
||
|
||
# Create varying parameters and objectives. | ||
var_1 = VaryingParameter("x0", -3.0, 3.0) | ||
var_2 = VaryingParameter("x1", -2.0, 2.0) | ||
obj = Objective("f") | ||
|
||
n = 2 | ||
|
||
aposmm = APOSMM( | ||
initial_sample_size=100, | ||
localopt_method="LN_BOBYQA", | ||
sample_points=np.round(minima, 1), | ||
rk_const=0.5 * ((gamma(1 + (n / 2)) * 5) ** (1 / n)) / sqrt(pi), | ||
xtol_abs=1e-2, | ||
ftol_abs=1e-2, | ||
dist_to_bound_multiple=0.5, | ||
max_active_runs=4, # refers to APOSMM's simul local optimization runs | ||
lb=np.array([-3, -2]), # potentially matches the VaryingParameters | ||
ub=np.array([3, 2]), | ||
) | ||
|
||
gen = libEWrapper( | ||
varying_parameters=[var_1, var_2], | ||
objectives=[obj], | ||
libe_gen_instance=aposmm, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How different to libe_gen. This can be set up so if a class is provided it initializes in place. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's true, for rough-development I was being explicit on what type of object we passed, but we can afford to be smarter now. Pass in something initialized, keep as-is, if its not initialized, initialize it. I'll take a second look, but I don't think I came across a pure-optimas example that did object-initialization within the library instead of exposed to the user. |
||
) | ||
|
||
# Create evaluator. | ||
ev = TemplateEvaluator( | ||
sim_template="template_simulation_script.py", | ||
analysis_func=analyze_simulation, | ||
) | ||
|
||
|
||
# Create exploration. | ||
exp = Exploration( | ||
generator=gen, evaluator=ev, max_evals=300, sim_workers=4, run_async=True | ||
) | ||
|
||
|
||
# To safely perform exploration, run it in the block below (this is needed | ||
# for some flavours of multiprocessing, namely spawn and forkserver) | ||
if __name__ == "__main__": | ||
exp.run() | ||
assert len(gen.libe_gen.all_local_minima) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we want to check the minima have the expected values. |
||
print(f"Found {len(gen.libe_gen.all_local_minima)} minima!") |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
"""Simple template script used for demonstration. | ||
|
||
The script evaluates an analytical expression and stores the results in a | ||
`result.txt` file that is later read by the analysis function. | ||
""" | ||
|
||
import numpy as np | ||
|
||
# 2D function with multiple minima | ||
# result = -({{x0}} + 10 * np.cos({{x0}})) * ({{x1}} + 5 * np.cos({{x1}})) | ||
|
||
x1 = {{x0}} | ||
x2 = {{x1}} | ||
|
||
term1 = (4 - 2.1 * x1**2 + (x1**4) / 3) * x1**2 | ||
term2 = x1 * x2 | ||
term3 = (-4 + 4 * x2**2) * x2**2 | ||
|
||
result = term1 + term2 + term3 | ||
|
||
with open("result.txt", "w") as f: | ||
f.write("%f" % result) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
"""Basic example of parallel random sampling with simulations.""" | ||
|
||
from libensemble.gen_funcs.persistent_sampling import RandSample | ||
from optimas.core import VaryingParameter, Objective | ||
|
||
# from optimas.generators import RandomSamplingGenerator | ||
from optimas.generators import libEWrapper | ||
from optimas.evaluators import TemplateEvaluator | ||
from optimas.explorations import Exploration | ||
|
||
|
||
def analyze_simulation(simulation_directory, output_params): | ||
"""Analyze the simulation output. | ||
|
||
This method analyzes the output generated by the simulation to | ||
obtain the value of the optimization objective and other analyzed | ||
parameters, if specified. The value of these parameters has to be | ||
given to the `output_params` dictionary. | ||
|
||
Parameters | ||
---------- | ||
simulation_directory : str | ||
Path to the simulation folder where the output was generated. | ||
output_params : dict | ||
Dictionary where the value of the objectives and analyzed parameters | ||
will be stored. There is one entry per parameter, where the key | ||
is the name of the parameter given by the user. | ||
|
||
Returns | ||
------- | ||
dict | ||
The `output_params` dictionary with the results from the analysis. | ||
|
||
""" | ||
# Read back result from file | ||
with open("result.txt") as f: | ||
result = float(f.read()) | ||
# Fill in output parameters. | ||
output_params["f"] = result | ||
return output_params | ||
|
||
|
||
# Create varying parameters and objectives. | ||
var_1 = VaryingParameter("x0", 0.0, 15.0) | ||
var_2 = VaryingParameter("x1", 0.0, 15.0) | ||
obj = Objective("f") | ||
|
||
|
||
# Create generator. | ||
# gen = RandomSamplingGenerator( | ||
# varying_parameters=[var_1, var_2], objectives=[obj], distribution="normal" | ||
# ) | ||
|
||
gen = libEWrapper( | ||
varying_parameters=[var_1, var_2], | ||
objectives=[obj], | ||
libe_gen_class=RandSample, | ||
) | ||
|
||
# Create evaluator. | ||
ev = TemplateEvaluator( | ||
sim_template="template_simulation_script.py", | ||
analysis_func=analyze_simulation, | ||
) | ||
|
||
|
||
# Create exploration. | ||
exp = Exploration( | ||
generator=gen, evaluator=ev, max_evals=10, sim_workers=4, run_async=True | ||
) | ||
|
||
|
||
# To safely perform exploration, run it in the block below (this is needed | ||
# for some flavours of multiprocessing, namely spawn and forkserver) | ||
if __name__ == "__main__": | ||
exp.run() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
"""Simple template script used for demonstration. | ||
|
||
The script evaluates an analytical expression and stores the results in a | ||
`result.txt` file that is later read by the analysis function. | ||
""" | ||
|
||
import numpy as np | ||
|
||
# 2D function with multiple minima | ||
result = -({{x0}} + 10 * np.cos({{x0}})) * ({{x1}} + 5 * np.cos({{x1}})) | ||
|
||
with open("result.txt", "w") as f: | ||
f.write("%f" % result) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lb/ub duplicated from VaryingParameter. Not sure best approach, but for now we could define these first and use in both places.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup! Perhaps our generator_standard work may help this avenue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Insert same source-of-truth values into VaryingParameters and ub and lb