Source code for pycif.plugins.minimizers.congrad.checks

import os
import pickle
import sys
import time
from logging import info, warning

import numpy as np
import pandas as pd

from ....utils import path
from ....utils.yml import ordered_dump


[docs] def init_check_files(self): """Set the paths of the files monitoring the convergence and back up the ones from a previous run.""" simulator_dir = self.simulator.simulator_dir self.check_cost_file = simulator_dir / "congrad_checks_cost.csv" self.check_gradcost_file = simulator_dir / "congrad_checks_gradcost.csv" for f in (self.check_cost_file, self.check_gradcost_file): if f.exists(): f.replace(f.with_suffix(f"{f.suffix}.bak")) self.pending_checks = []
[docs] def write_predicted(self, niter, chi, cost, grad): r"""Write the cost function and gradient predicted by the quadratic model at the current estimate of the minimum. Args: niter (int): number of Lanczos iterations performed so far. chi (np.ndarray): current estimate of the minimum. cost (float): predicted cost function. grad (np.ndarray): predicted gradient. """ run_id = _run_id(niter) # Jb = 1/2 chi'.chi and grad(Jb) = chi are exact j_b = 0.5 * np.dot(chi, chi) znorm_grad = np.sqrt(np.dot(grad, grad)) info( f"CONGRAD predicted values after {niter} iterations:\n" f" Jb = {j_b}\n" f" Jo = {cost - j_b}\n" f" Total = {cost}\n" f" grad(J) = {znorm_grad}" ) _write_csv( self.check_cost_file, run_id, {"j_b_pred": j_b, "j_o_pred": cost - j_b, "zcost_pred": cost}, ) _write_csv( self.check_gradcost_file, run_id, { "znorm_grad_b_pred": np.sqrt(np.dot(chi, chi)), "znorm_grad_o_pred": np.sqrt(np.dot(grad - chi, grad - chi)), "znorm_grad_pred": znorm_grad, }, )
[docs] def dump_state(self, chi, niter, **kwargs): """Dump the control vector at the current estimate of the minimum. Args: chi (np.ndarray): current estimate of the minimum. niter (int): number of Lanczos iterations performed so far. **kwargs: forwarded to ``sqrtbprod``. """ controlvect = self.simulator.controlvect check_dir = _check_dir(self, niter) path.init_dir(check_dir) # Keep the control vector of the Lanczos iterations untouched x_lanczos = controlvect.x controlvect.x = controlvect.sqrtbprod(chi, **kwargs) controlvect.dump( f"{check_dir}/controlvect.pickle", to_netcdf=controlvect.save_out_netcdf, dir_netcdf=f"{check_dir}/controlvect/", ) controlvect.x = x_lanczos
[docs] def submit_check(self, chi, niter): r"""Submit a job evaluating the cost function and its gradient at the current estimate of the minimum. Args: chi (np.ndarray): current estimate of the minimum. niter (int): number of Lanczos iterations performed so far. """ platform = self.platform workdir = self.simulator.workdir run_id = _run_id(niter) # Separate working directory to keep the dumped state apart eval_dir = f"{_check_dir(self, niter)}/evaluation/" path.init_dir(eval_dir) check = { "run_id": run_id, "niter": niter, "job_id": None, "result_file": f"{eval_dir}/simulator/cost_grad.{run_id}.pickle", } # Already computed in a previous run if os.path.isfile(check["result_file"]): self.pending_checks.append(check) return chi_file = f"{eval_dir}/chi.npy" np.save(chi_file, chi) # Same configuration as the main run, without the mode and with the # simulator at the top level to only evaluate the cost function yml_dict = self.from_yaml( self.reference_instances["reference_setup"].def_file) mode_dict = yml_dict.pop("mode") simulator_dict = mode_dict.get("minimizer", {}).get( "simulator", mode_dict.get("simulator", yml_dict.get("simulator", {})) ) yml_dict["simulator"] = { **simulator_dict, "plugin": {"name": self.simulator.plugin.name, "version": self.simulator.plugin.version}, } yml_dict["workdir"] = eval_dir yml_dict["return_config"] = True yml_dict["obsvect"] = { **yml_dict.get("obsvect", {}), "dir_obsvect": f"{workdir}/obsvect/", } del yml_dict["def_file"] yml_file = f"{eval_dir}/config_{run_id}.yml" with open(yml_file, "w") as outfile: ordered_dump(outfile, yml_dict) python = getattr(platform, "python", sys.executable) exe = (f"{python} -m pycif.plugins.minimizers.congrad.check_job " f"{yml_file} {chi_file} {run_id}") if self.force_linearize: exe += " --linear" info(f"Submitting CONGRAD check after {niter} iterations in {eval_dir}") check["job_id"] = platform.submit_job( exe, os.path.join(eval_dir, f"job_pycif_{run_id}")) self.pending_checks.append(check)
[docs] def collect_checks(self, wait=False): """Process the evaluated checks whose job is over. Args: wait (bool): wait for all pending checks to be over. """ platform = self.platform while self.pending_checks: remaining = [] for check in self.pending_checks: if check["job_id"] is not None \ and not platform.check_jobs([check["job_id"]]): remaining.append(check) continue _process_check(self, check) self.pending_checks = remaining if not wait or not remaining: break time.sleep(platform.sleep_time)
def _run_id(niter): return f"check_{niter:04d}" def _check_dir(self, niter): return f"{self.simulator.workdir}/congrad_checks/{_run_id(niter)}/" def _process_check(self, check): run_id = check["run_id"] # Checks are only diagnostics: a failure does not stop the minimization if not os.path.isfile(check["result_file"]): warning( f"CONGRAD check {run_id} failed. Please check the simulation in " f"{os.path.dirname(os.path.dirname(check['result_file']))}" ) return with open(check["result_file"], "rb") as f: j_b, j_o, zgrad_b, zgrad_o = pickle.load(f) zcost = j_b + j_o zgrad = zgrad_b + zgrad_o znorm_grad = np.sqrt(np.dot(zgrad, zgrad)) info( f"CONGRAD evaluated values after {check['niter']} iterations:\n" f" Jb = {j_b}\n" f" Jo = {j_o}\n" f" Total = {zcost}\n" f" grad(J) = {znorm_grad}" ) _write_csv( self.check_cost_file, run_id, {"j_b": j_b, "j_o": j_o, "zcost": zcost}, ) _write_csv( self.check_gradcost_file, run_id, { "znorm_grad_b": np.sqrt(np.dot(zgrad_b, zgrad_b)), "znorm_grad_o": np.sqrt(np.dot(zgrad_o, zgrad_o)), "znorm_grad": znorm_grad, }, ) def _write_csv(csv_file, run_id, values): if csv_file.exists(): df = pd.read_csv(csv_file, index_col="run_id") else: df = pd.DataFrame(columns=[*values, "time"]) df.index.name = "run_id" for key, value in values.items(): df.loc[run_id, key] = value df.loc[run_id, "time"] = pd.Timestamp.now().isoformat() df.to_csv(csv_file)