From 0657efdd66cf6a817762403897cfd833050b5a12 Mon Sep 17 00:00:00 2001 From: jlnav Date: Fri, 28 Mar 2025 12:26:29 -0500 Subject: [PATCH 1/9] various fixes to generators.py, aposmm.py to get ibcdfo starting to work with optimas --- libensemble/gen_classes/aposmm.py | 2 +- libensemble/generators.py | 32 +++++++++++++++++-------------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/libensemble/gen_classes/aposmm.py b/libensemble/gen_classes/aposmm.py index 4adac2c2a..0bfa82cea 100644 --- a/libensemble/gen_classes/aposmm.py +++ b/libensemble/gen_classes/aposmm.py @@ -42,7 +42,7 @@ def __init__( ("local_min", bool), ("local_pt", bool), ] - gen_specs["persis_in"] = ["x", "f", "local_pt", "sim_id", "sim_ended", "x_on_cube", "local_min"] + gen_specs["persis_in"] = ["x", "f", "fvec", "local_pt", "sim_id", "sim_ended", "x_on_cube", "local_min"] super().__init__(variables, objectives, History, persis_info, gen_specs, libE_info, **kwargs) if not self.persis_info.get("nworkers"): self.persis_info["nworkers"] = kwargs.get("nworkers", gen_specs["user"]["max_active_runs"]) diff --git a/libensemble/generators.py b/libensemble/generators.py index 22f26b782..1f7a2fc14 100644 --- a/libensemble/generators.py +++ b/libensemble/generators.py @@ -116,18 +116,18 @@ def __init__( self._internal_variable = "x" # need to figure these out dynamically self._internal_objective = "f" - if self.variables: - - self.n = len(self.variables) - # build our own lb and ub - lb = [] - ub = [] - for i, v in enumerate(self.variables.values()): - if isinstance(v, list) and (isinstance(v[0], int) or isinstance(v[0], float)): - lb.append(v[0]) - ub.append(v[1]) - kwargs["lb"] = np.array(lb) - kwargs["ub"] = np.array(ub) + # if self.variables: + + # self.n = len(self.variables) + # # build our own lb and ub + # lb = [] + # ub = [] + # for i, v in enumerate(self.variables.values()): + # if isinstance(v, list) and (isinstance(v[0], int) or isinstance(v[0], float)): + # lb.append(v[0]) + # ub.append(v[1]) + # kwargs["lb"] = np.array(lb) + # kwargs["ub"] = np.array(ub) if len(kwargs) > 0: # so user can specify gen-specific parameters as kwargs to constructor if not self.gen_specs.get("user"): @@ -208,8 +208,12 @@ def setup(self) -> None: self.libE_info["comm"] = self.running_gen_f.comm def _set_sim_ended(self, results: npt.NDArray) -> npt.NDArray: - new_results = np.zeros(len(results), dtype=self.gen_specs["out"] + [("sim_ended", bool), ("f", float)]) - for field in results.dtype.names: + filtered_dtype = [ + (name, results.dtype[name]) for name in results.dtype.names if name in self.gen_specs["persis_in"] + ] + new_dtype = filtered_dtype + [("sim_ended", bool)] + new_results = np.zeros(len(results), dtype=new_dtype) + for field in new_results.dtype.names: # only copy over fields that generator explicitly wants try: new_results[field] = results[field] except ValueError: # lets not slot in data that the gen doesnt need? From 96ee0d71a4a830ce1baff0f5389ed90e7f3b4bac Mon Sep 17 00:00:00 2001 From: jlnav Date: Wed, 2 Apr 2025 15:40:19 -0500 Subject: [PATCH 2/9] add libgfortran5 for macOS --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 7de7e37c3..cb24efd9f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -95,6 +95,7 @@ psutil = ">=5.9.4,<7" [tool.pixi.target.osx-arm64.dependencies] clang_osx-arm64 = ">=19.1.2,<20" +libgfortran5 = ">=14.2.0,<15" [tool.black] line-length = 120 From f610739191e5f69fac2235fd65302c19dbe77b33 Mon Sep 17 00:00:00 2001 From: shudson Date: Wed, 2 Apr 2025 16:57:09 -0500 Subject: [PATCH 3/9] Add support for hfun arguments in APSOMM --- libensemble/gen_classes/aposmm.py | 12 +++++++++++- libensemble/gen_funcs/persistent_aposmm.py | 20 +++++++++++++++++--- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/libensemble/gen_classes/aposmm.py b/libensemble/gen_classes/aposmm.py index 0bfa82cea..0b340eaa8 100644 --- a/libensemble/gen_classes/aposmm.py +++ b/libensemble/gen_classes/aposmm.py @@ -42,7 +42,17 @@ def __init__( ("local_min", bool), ("local_pt", bool), ] - gen_specs["persis_in"] = ["x", "f", "fvec", "local_pt", "sim_id", "sim_ended", "x_on_cube", "local_min"] + gen_specs["persis_in"] = [ + "x", + "f", + "fvec", + "local_pt", + "sim_id", + "sim_ended", + "x_on_cube", + "local_min", + "hfun_arg", + ] super().__init__(variables, objectives, History, persis_info, gen_specs, libE_info, **kwargs) if not self.persis_info.get("nworkers"): self.persis_info["nworkers"] = kwargs.get("nworkers", gen_specs["user"]["max_active_runs"]) diff --git a/libensemble/gen_funcs/persistent_aposmm.py b/libensemble/gen_funcs/persistent_aposmm.py index 1dc3af8e1..3fb016167 100644 --- a/libensemble/gen_funcs/persistent_aposmm.py +++ b/libensemble/gen_funcs/persistent_aposmm.py @@ -34,7 +34,7 @@ def cdist(XA, XB, metric="euclidean"): distances = np.sqrt(np.sum(diff**2, axis=2)) return distances - +# SH TODO hfun_arg could be a list of arguments def aposmm(H, persis_info, gen_specs, libE_info): """ APOSMM coordinates multiple local optimization runs, dramatically reducing time for @@ -53,6 +53,7 @@ def aposmm(H, persis_info, gen_specs, libE_info): - ``"fvec" [m floats]``: All objective components (if performing a least-squares calculation) - ``"grad" [n floats]``: The gradient (if available) of the objective with respect to `x`. + - ``"hfun_arg" [any]``: The argument of the hfun function. Note: @@ -208,6 +209,14 @@ def aposmm(H, persis_info, gen_specs, libE_info): n_s, n_r = update_local_H_after_receiving(local_H, n, n_s, user_specs, Work, calc_in, fields_to_pass) + if 'hfun' in user_specs and 'hfun_arg' in calc_in.dtype.names: + # closure to capture the hfun_arg + def create_hfun_wrapper(original_hfun, hfun_arg): + def hfun(x): + return original_hfun(x, hfun_arg) + return hfun + user_specs['hfun'] = create_hfun_wrapper(user_specs['hfun'], calc_in['hfun_arg']) + for row in calc_in: if sim_id_to_child_inds.get(row["sim_id"]): # Point came from a child local opt run @@ -233,6 +242,8 @@ def aposmm(H, persis_info, gen_specs, libE_info): starting_inds = decide_where_to_start_localopt(local_H, n, n_s, rk_const, ld, mu, nu) + + for ind in starting_inds: if len([p for p in local_opters.values() if p.is_running]) < user_specs.get("max_active_runs", np.inf): local_H["started_run"][ind] = 1 @@ -243,6 +254,7 @@ def aposmm(H, persis_info, gen_specs, libE_info): local_H[ind]["x_on_cube"], local_H[ind]["f"] if "f" in fields_to_pass else local_H[ind]["fvec"], local_H[ind]["grad"] if "grad" in fields_to_pass else None, + ) local_opters[total_runs] = local_opter @@ -287,12 +299,13 @@ def aposmm(H, persis_info, gen_specs, libE_info): def update_local_H_after_receiving(local_H, n, n_s, user_specs, Work, calc_in, fields_to_pass): - for name in ["f", "x_on_cube", "grad", "fvec"]: + for name in ["f", "x_on_cube", "grad", "fvec", "hfun_arg"]: if name in fields_to_pass: assert name in calc_in.dtype.names, ( name + " must be returned to persistent_aposmm for localopt_method: " + user_specs["localopt_method"] ) + for name in calc_in.dtype.names: local_H[name][Work["libE_info"]["H_rows"]] = calc_in[name] @@ -648,6 +661,7 @@ def initialize_APOSMM(H, user_specs, libE_info): local_H_fields = [ ("f", float), ("grad", float, n), + ("hfun_arg", float), ("x", float, n), ("x_on_cube", float, n), ("local_pt", bool), @@ -735,7 +749,7 @@ def initialize_children(user_specs): ]: fields_to_pass = ["x_on_cube", "f"] elif user_specs["localopt_method"] in ["pounders", "ibcdfo_pounders", "dfols"]: - fields_to_pass = ["x_on_cube", "fvec"] + fields_to_pass = ["x_on_cube", "fvec", "hfun_arg"] else: raise NotImplementedError(f"Unknown local optimization method {user_specs['localopt_method']}.") From 8910b3774266f6be891966be6b424a3f2e5bb600 Mon Sep 17 00:00:00 2001 From: shudson Date: Wed, 2 Apr 2025 16:58:48 -0500 Subject: [PATCH 4/9] Specify as float to be consistent with local_H --- libensemble/gen_funcs/persistent_aposmm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libensemble/gen_funcs/persistent_aposmm.py b/libensemble/gen_funcs/persistent_aposmm.py index 3fb016167..91cf1e815 100644 --- a/libensemble/gen_funcs/persistent_aposmm.py +++ b/libensemble/gen_funcs/persistent_aposmm.py @@ -53,7 +53,7 @@ def aposmm(H, persis_info, gen_specs, libE_info): - ``"fvec" [m floats]``: All objective components (if performing a least-squares calculation) - ``"grad" [n floats]``: The gradient (if available) of the objective with respect to `x`. - - ``"hfun_arg" [any]``: The argument of the hfun function. + - ``"hfun_arg" [float]``: The argument of the hfun function. Note: From d1efcfb5bef443eb2811e1edb50d7887e44e8f7c Mon Sep 17 00:00:00 2001 From: shudson Date: Fri, 4 Apr 2025 12:45:14 -0500 Subject: [PATCH 5/9] Revert "Specify as float to be consistent with local_H" This reverts commit 8910b3774266f6be891966be6b424a3f2e5bb600. --- libensemble/gen_funcs/persistent_aposmm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libensemble/gen_funcs/persistent_aposmm.py b/libensemble/gen_funcs/persistent_aposmm.py index 91cf1e815..3fb016167 100644 --- a/libensemble/gen_funcs/persistent_aposmm.py +++ b/libensemble/gen_funcs/persistent_aposmm.py @@ -53,7 +53,7 @@ def aposmm(H, persis_info, gen_specs, libE_info): - ``"fvec" [m floats]``: All objective components (if performing a least-squares calculation) - ``"grad" [n floats]``: The gradient (if available) of the objective with respect to `x`. - - ``"hfun_arg" [float]``: The argument of the hfun function. + - ``"hfun_arg" [any]``: The argument of the hfun function. Note: From 8bce0a6b773d748ac86efb9f84749085ccd3ab8d Mon Sep 17 00:00:00 2001 From: shudson Date: Fri, 4 Apr 2025 12:51:48 -0500 Subject: [PATCH 6/9] Revert "Add support for hfun arguments in APSOMM" This reverts commit f610739191e5f69fac2235fd65302c19dbe77b33. --- libensemble/gen_classes/aposmm.py | 12 +----------- libensemble/gen_funcs/persistent_aposmm.py | 20 +++----------------- 2 files changed, 4 insertions(+), 28 deletions(-) diff --git a/libensemble/gen_classes/aposmm.py b/libensemble/gen_classes/aposmm.py index 0b340eaa8..0bfa82cea 100644 --- a/libensemble/gen_classes/aposmm.py +++ b/libensemble/gen_classes/aposmm.py @@ -42,17 +42,7 @@ def __init__( ("local_min", bool), ("local_pt", bool), ] - gen_specs["persis_in"] = [ - "x", - "f", - "fvec", - "local_pt", - "sim_id", - "sim_ended", - "x_on_cube", - "local_min", - "hfun_arg", - ] + gen_specs["persis_in"] = ["x", "f", "fvec", "local_pt", "sim_id", "sim_ended", "x_on_cube", "local_min"] super().__init__(variables, objectives, History, persis_info, gen_specs, libE_info, **kwargs) if not self.persis_info.get("nworkers"): self.persis_info["nworkers"] = kwargs.get("nworkers", gen_specs["user"]["max_active_runs"]) diff --git a/libensemble/gen_funcs/persistent_aposmm.py b/libensemble/gen_funcs/persistent_aposmm.py index 3fb016167..1dc3af8e1 100644 --- a/libensemble/gen_funcs/persistent_aposmm.py +++ b/libensemble/gen_funcs/persistent_aposmm.py @@ -34,7 +34,7 @@ def cdist(XA, XB, metric="euclidean"): distances = np.sqrt(np.sum(diff**2, axis=2)) return distances -# SH TODO hfun_arg could be a list of arguments + def aposmm(H, persis_info, gen_specs, libE_info): """ APOSMM coordinates multiple local optimization runs, dramatically reducing time for @@ -53,7 +53,6 @@ def aposmm(H, persis_info, gen_specs, libE_info): - ``"fvec" [m floats]``: All objective components (if performing a least-squares calculation) - ``"grad" [n floats]``: The gradient (if available) of the objective with respect to `x`. - - ``"hfun_arg" [any]``: The argument of the hfun function. Note: @@ -209,14 +208,6 @@ def aposmm(H, persis_info, gen_specs, libE_info): n_s, n_r = update_local_H_after_receiving(local_H, n, n_s, user_specs, Work, calc_in, fields_to_pass) - if 'hfun' in user_specs and 'hfun_arg' in calc_in.dtype.names: - # closure to capture the hfun_arg - def create_hfun_wrapper(original_hfun, hfun_arg): - def hfun(x): - return original_hfun(x, hfun_arg) - return hfun - user_specs['hfun'] = create_hfun_wrapper(user_specs['hfun'], calc_in['hfun_arg']) - for row in calc_in: if sim_id_to_child_inds.get(row["sim_id"]): # Point came from a child local opt run @@ -242,8 +233,6 @@ def hfun(x): starting_inds = decide_where_to_start_localopt(local_H, n, n_s, rk_const, ld, mu, nu) - - for ind in starting_inds: if len([p for p in local_opters.values() if p.is_running]) < user_specs.get("max_active_runs", np.inf): local_H["started_run"][ind] = 1 @@ -254,7 +243,6 @@ def hfun(x): local_H[ind]["x_on_cube"], local_H[ind]["f"] if "f" in fields_to_pass else local_H[ind]["fvec"], local_H[ind]["grad"] if "grad" in fields_to_pass else None, - ) local_opters[total_runs] = local_opter @@ -299,13 +287,12 @@ def hfun(x): def update_local_H_after_receiving(local_H, n, n_s, user_specs, Work, calc_in, fields_to_pass): - for name in ["f", "x_on_cube", "grad", "fvec", "hfun_arg"]: + for name in ["f", "x_on_cube", "grad", "fvec"]: if name in fields_to_pass: assert name in calc_in.dtype.names, ( name + " must be returned to persistent_aposmm for localopt_method: " + user_specs["localopt_method"] ) - for name in calc_in.dtype.names: local_H[name][Work["libE_info"]["H_rows"]] = calc_in[name] @@ -661,7 +648,6 @@ def initialize_APOSMM(H, user_specs, libE_info): local_H_fields = [ ("f", float), ("grad", float, n), - ("hfun_arg", float), ("x", float, n), ("x_on_cube", float, n), ("local_pt", bool), @@ -749,7 +735,7 @@ def initialize_children(user_specs): ]: fields_to_pass = ["x_on_cube", "f"] elif user_specs["localopt_method"] in ["pounders", "ibcdfo_pounders", "dfols"]: - fields_to_pass = ["x_on_cube", "fvec", "hfun_arg"] + fields_to_pass = ["x_on_cube", "fvec"] else: raise NotImplementedError(f"Unknown local optimization method {user_specs['localopt_method']}.") From d33bc1a6d8380f131af810454da6f707b26cbe30 Mon Sep 17 00:00:00 2001 From: jlnav Date: Fri, 11 Apr 2025 14:44:43 -0500 Subject: [PATCH 7/9] accept_fvec parameter appends fvec to gen_specs.persis_in --- libensemble/gen_classes/aposmm.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libensemble/gen_classes/aposmm.py b/libensemble/gen_classes/aposmm.py index 0bfa82cea..75548c293 100644 --- a/libensemble/gen_classes/aposmm.py +++ b/libensemble/gen_classes/aposmm.py @@ -21,6 +21,7 @@ def __init__( persis_info: dict = {}, gen_specs: dict = {}, libE_info: dict = {}, + accept_fvec: bool = False, **kwargs, ) -> None: from libensemble.gen_funcs.persistent_aposmm import aposmm @@ -42,7 +43,9 @@ def __init__( ("local_min", bool), ("local_pt", bool), ] - gen_specs["persis_in"] = ["x", "f", "fvec", "local_pt", "sim_id", "sim_ended", "x_on_cube", "local_min"] + gen_specs["persis_in"] = ["x", "f", "local_pt", "sim_id", "sim_ended", "x_on_cube", "local_min"] + if accept_fvec: + gen_specs["persis_in"].append("fvec") super().__init__(variables, objectives, History, persis_info, gen_specs, libE_info, **kwargs) if not self.persis_info.get("nworkers"): self.persis_info["nworkers"] = kwargs.get("nworkers", gen_specs["user"]["max_active_runs"]) From d6e0c609b2a6b7a2f9f31701fd91b8950dc604aa Mon Sep 17 00:00:00 2001 From: shudson Date: Mon, 28 Apr 2025 15:13:15 -0500 Subject: [PATCH 8/9] Pass fvec based on components mirroring APOSMM --- libensemble/gen_classes/aposmm.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libensemble/gen_classes/aposmm.py b/libensemble/gen_classes/aposmm.py index 75548c293..064e2ebf6 100644 --- a/libensemble/gen_classes/aposmm.py +++ b/libensemble/gen_classes/aposmm.py @@ -21,7 +21,6 @@ def __init__( persis_info: dict = {}, gen_specs: dict = {}, libE_info: dict = {}, - accept_fvec: bool = False, **kwargs, ) -> None: from libensemble.gen_funcs.persistent_aposmm import aposmm @@ -44,7 +43,7 @@ def __init__( ("local_pt", bool), ] gen_specs["persis_in"] = ["x", "f", "local_pt", "sim_id", "sim_ended", "x_on_cube", "local_min"] - if accept_fvec: + if "components" in kwargs: gen_specs["persis_in"].append("fvec") super().__init__(variables, objectives, History, persis_info, gen_specs, libE_info, **kwargs) if not self.persis_info.get("nworkers"): From 91a94138de03edd641e72d5c4f74517ac0e18c12 Mon Sep 17 00:00:00 2001 From: shudson Date: Mon, 28 Apr 2025 15:18:44 -0500 Subject: [PATCH 9/9] Components could be in gen_specs user --- libensemble/gen_classes/aposmm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libensemble/gen_classes/aposmm.py b/libensemble/gen_classes/aposmm.py index 064e2ebf6..0401399a3 100644 --- a/libensemble/gen_classes/aposmm.py +++ b/libensemble/gen_classes/aposmm.py @@ -43,7 +43,7 @@ def __init__( ("local_pt", bool), ] gen_specs["persis_in"] = ["x", "f", "local_pt", "sim_id", "sim_ended", "x_on_cube", "local_min"] - if "components" in kwargs: + if "components" in kwargs or "components" in gen_specs.get("user", {}): gen_specs["persis_in"].append("fvec") super().__init__(variables, objectives, History, persis_info, gen_specs, libE_info, **kwargs) if not self.persis_info.get("nworkers"):