diff --git a/xopt/base.py b/xopt/base.py index 08a3533c3..af0709187 100644 --- a/xopt/base.py +++ b/xopt/base.py @@ -11,6 +11,7 @@ # from xopt.generators import get_generator_and_defaults from xopt.pydantic import XoptBaseModel +from xopt.utils import build_generator_from_saved_state from xopt.vocs import VOCS __version__ = _version.get_versions()["version"] @@ -66,7 +67,8 @@ def __init__( Initialize Xopt object using either a config dictionary or explicitly Args: - config: dict, or YAML or JSON str or file. This overrides all other arguments. + config: dict, or YAML or JSON str or file. + This overrides all other arguments. generator: Generator object evaluator: Evaluator object @@ -310,10 +312,17 @@ def check_components(self): if self.vocs is None: raise XoptError("Xopt VOCS is not specified") + def rebuild_from_previous_state(self, index): + """rebuild generator from saved history""" + if self.options.dump_file is not None: + return build_generator_from_saved_state( + index=index, dump_file=self.options.dump_file + ) + def dump_state(self): """dump data to file""" if self.options.dump_file is not None: - output = state_to_dict(self) + output = state_to_dict(self, include_history=True) with open(self.options.dump_file, "w") as f: yaml.dump(output, f) logger.debug(f"Dumped state to YAML file: {self.options.dump_file}") @@ -394,7 +403,8 @@ def yaml(self, filename=None, *, include_data=False): def __repr__(self): """ - Returns infor about the Xopt object, including the YAML representation without data. + Returns infor about the Xopt object, + including the YAML representation without data. """ return f""" Xopt @@ -489,7 +499,7 @@ def xopt_kwargs_from_dict(config: dict) -> dict: } -def state_to_dict(X, include_data=True): +def state_to_dict(X, include_data=True, include_history=False): # dump data to dict with config metadata output = { "xopt": json.loads(X.options.json()), @@ -503,4 +513,7 @@ def state_to_dict(X, include_data=True): if include_data: output["data"] = json.loads(X.data.to_json()) + if include_history: + output["history"] = json.loads(X.generator.json()) + return output diff --git a/xopt/generator.py b/xopt/generator.py index 3374af5dd..8fa6fb64d 100644 --- a/xopt/generator.py +++ b/xopt/generator.py @@ -46,6 +46,10 @@ def __init__(self, **kwargs): vocs: The vocs to use. options: The options to use. """ + print("start") + for key, value in kwargs.items(): + print(key, value) + print("end") super().__init__(**kwargs) _check_vocs(self.vocs, self.supports_multi_objective) logger.info(f"Initialized generator {self.name}") diff --git a/xopt/pydantic.py b/xopt/pydantic.py index 60fd9601c..169ffc324 100644 --- a/xopt/pydantic.py +++ b/xopt/pydantic.py @@ -421,7 +421,8 @@ def map(self, fn, *iter: Iterable, **kwargs) -> Iterable[Future]: def get_callable_from_string(callable: str, bind: Any = None) -> Callable: - """Get callable from a string. In the case that the callable points to a bound method, + """Get callable from a string. + In the case that the callable points to a bound method, the function returns a callable taking the bind instance as the first arg. Args: diff --git a/xopt/utils.py b/xopt/utils.py index 31d0b6b8f..73d24d943 100644 --- a/xopt/utils.py +++ b/xopt/utils.py @@ -8,6 +8,9 @@ import pandas as pd import torch import yaml +from pydantic import parse_obj_as + +from xopt.generators import get_generator from .pydantic import get_descriptions_defaults from .vocs import VOCS @@ -172,6 +175,21 @@ def read_xopt_csv(*files): return pd.concat(dfs) +def build_generator_from_saved_state(index, dump_file): + """rebuild generator from saved history""" + with open(dump_file, "r") as file: + data = yaml.safe_load(file) + + list_of_saved_generators = data["history"] + desired_state = list_of_saved_generators[index] + + # desired_state['vocs'] = data['vocs'] + generator_class = get_generator(data["generator"].pop("name")) + rebuilt_generator = parse_obj_as(generator_class, desired_state) + + return rebuilt_generator + + def visualize_model(generator, data, axes=None): test_x = torch.linspace(*torch.tensor(generator.vocs.bounds.flatten()), 100) generator.add_data(data)