Source code for smt_optim.utils.logger

import os
import json

import numpy as np

from .json import json_safe

from smt_optim.utils.multi_obj import hypervolume, spacing, get_pf_from_dataset


[docs] def format_value(v, fmt): if isinstance(v, float): return format(v, fmt) return str(v)
[docs] class ConsoleLogger: def __init__(self, config): self.config = config self.headers = [ "iter", "budget", "fmin", "rscv", "fidelity", "gp_time", "acq_time", ] self.widths: list | None = None self.header_fmt: str | None = None self.row_fmt: str | None = None self.update_header_format() self.formats = { "iter": ".0f", "budget": ".3f", "fmin": ".5e", "HV": ".5e", # hypervolume (for multi-obj only) "spacing": ".5e", # spacing (for multi-obj only) "rscv": ".3e", "fidelity": ".0f", "gp_time": ".3f", "acq_time": ".3f", } self.iter = 0 self.repeat_header = 10 self.multi_obj_ref = None # reference objective values (for multi-obj only)
[docs] def on_iter_end(self, state) -> None: if self.iter % self.repeat_header == 0: if state.problem.num_obj > 1 and "fmin" in self.headers: self.headers[2] = "HV" self.headers[3] = "spacing" # self.headers.pop(3) self.update_header_format() dataset = state.dataset.export_as_dict() obj = dataset["obj"] dataset["rscv"] # self.multi_obj_ref = np.array([ # obj[:, 0][rscv <= 1e-4].max(), # obj[:, 1][rscv <= 1e-4].max(), # ]) self.multi_obj_ref = np.empty(state.problem.num_obj) for obj_idx in range(state.problem.num_obj): self.multi_obj_ref[obj_idx] = obj[:, obj_idx].max() self.print_header() sample = state.get_best_sample(ctol=1e-4) iter_log = getattr(state, "iter_log", {}) or {} data = { "iter": state.iter, "budget": state.budget, # "rscv": sample.metadata["rscv"], "fidelity": iter_log.get("fidelity", np.nan), "gp_time": iter_log.get("gp_training_time", np.nan), "acq_time": iter_log.get("acq_opt_time", np.nan), } if state.problem.num_obj == 1: data["fmin"] = sample.obj[0] data["rscv"] = sample.metadata["rscv"] elif state.problem.num_obj > 1: # dataset = state.dataset.export_as_dict() # obj = dataset["obj"] # rscv = dataset["rscv"] # obj = obj[rscv <= 1e-4, :] # pf = get_pareto_front(obj) pf = get_pf_from_dataset(state.dataset, ctol=1e-4) # hv_indicator = HV(ref_point=self.multi_obj_ref) if pf.shape[0] > 0: hv = hypervolume(pf, self.multi_obj_ref) sp = spacing(pf) else: hv = np.nan sp = np.nan data["HV"] = hv data["spacing"] = sp else: data["HV"] = np.nan data["spacing"] = np.nan row = [format_value(data[h], self.formats[h]) for h in self.headers] print(self.row_fmt.format(*row)) self.iter += 1
[docs] def print_header(self): print(self.header_fmt.format(*self.headers))
[docs] def update_header_format(self): width = 14 self.widths = [max(len(h), width) for h in self.headers] self.header_fmt = " ".join(f"{{:>{width}}}" for _ in self.headers) self.row_fmt = " ".join(f"{{:>{width}}}" for _ in self.headers)
[docs] class JsonLogger: def __init__(self, config): self.dir = config.results_dir
[docs] def on_iter_end(self, state) -> None: path = os.path.join(self.dir, "stats.jsonl") os.makedirs(self.dir, exist_ok=True) with open(path, "a") as file: safe_iter_log = json_safe(state.iter_log) file.write(json.dumps(safe_iter_log) + "\n")
# json.dump(safe_iter_log, file, indent=4) # class DoeLogger: # def __init__(self, config): # self.config = config # self.num_saved = 0 # # # def log_sample(self, state, sample) -> None: # """ # Log sample data once sampled # # :param state: # :param sample: # :return: # """ # # if self.config.results_dir is None: # return None # # try: # row = dict() # # row["iter"] = sample.metadata["iter"] # row["budget"] = np.nan # self.compute_used_budget() # self.budget # # # save variables # for i in range(len(sample.x)): # row[f"x{i}"] = sample.x[i] # # # save objectives # for i in range(len(sample.obj)): # row[f"f{i}"] = sample.obj[i] # # # save constraints # for i in range(len(sample.cstr)): # row[f"c{i}"] = sample.cstr[i] # # row["time"] = np.sum(sample.eval_time) # # path = os.path.join(self.config.results_dir, "doe.csv") # file_exists = os.path.isfile(path) # # # possibly does not work on Windows -> to be tested # with open(path, 'a') as file: # writer = csv.DictWriter(file, fieldnames=row.keys()) # # if not file_exists: # writer.writeheader() # # writer.writerow(row) # # self.num_saved += 1 # # except Exception as e: # print(f"Error while saving the DoE: {e}") # # pass # # def on_iter_end(self, state) -> None: # """ # DOE data should be logged right after sampling the blackbox function (to avoid loss of data) # # :param state: # :return: # """ # num_samples = len(state.dataset.samples) # # for idx in range(self.num_saved, num_samples): # # sample = state.dataset.samples[idx] # # try: # row = dict() # # row["iter"] = sample.metadata["iter"] # row["budget"] = np.nan # self.compute_used_budget() # self.budget # # # save variables # for i in range(len(sample.x)): # row[f"x{i}"] = sample.x[i] # # # save objectives # for i in range(len(sample.obj)): # row[f"f{i}"] = sample.obj[i] # # # save constraints # for i in range(len(sample.cstr)): # row[f"c{i}"] = sample.cstr[i] # # row["time"] = np.sum(sample.eval_time) # # path = os.path.join(self.config.results_dir, "DOE") # os.makedirs(path, exist_ok=True) # # path = os.path.join(self.config.results_dir, "DOE", f"doe_fidelity_{sample.fidelity}.csv") # file_exists = os.path.isfile(path) # # # possibly does not work on Windows -> to be tested # with open(path, 'a') as file: # writer = csv.DictWriter(file, fieldnames=row.keys()) # # if not file_exists: # writer.writeheader() # # writer.writerow(row) # # self.num_saved += 1 # # except Exception as e: # print(f"Error while saving the DoE: {e}")