How to use logging#
optimagic can keep a persistent log of the parameter and criterion values tried out by an optimizer in a sqlite database.
Turn logging on or off#
To enable logging, it suffices to provide a path to an sqlite database when calling maximize
or minimize
. The database does not have to exist, optimagic will generate it for you.
from pathlib import Path
import numpy as np
import optimagic as om
def sphere(params):
return params @ params
# Remove the log file if it exists (just needed for the example)
log_file = Path("my_log.db")
if log_file.exists():
log_file.unlink()
res = om.minimize(
fun=sphere,
params=np.arange(5),
algorithm="scipy_lbfgsb",
logging="my_log.db",
)
In case the SQLite file already exists, this will raise a FileExistsError
to prevent from accidentally polluting an existing database. If you want to reuse
an existing database on purpose, you must explicitly provide the corresponding option for if_database_exists
:
log_options = om.SQLiteLogOptions(
"my_log.db", if_database_exists=om.ExistenceStrategy.EXTEND
)
res = om.minimize(
fun=sphere,
params=np.arange(5),
algorithm="scipy_lbfgsb",
logging=log_options,
)
Make logging faster#
By default, we use a very safe mode of sqlite that makes it almost impossible to corrupt the database. Even if your computer is suddenly shut down or unplugged.
However, this makes writing logs rather slow, which becomes notable when the criterion function is very fast.
In that case, you can enable fast_logging
, which is still quite safe!
log_options = om.SQLiteLogOptions(
"my_log.db",
fast_logging=True,
if_database_exists=om.ExistenceStrategy.REPLACE,
)
res = om.minimize(
fun=sphere,
params=np.arange(5),
algorithm="scipy_lbfgsb",
logging=log_options,
)
Reading the log#
To read the log after an optimization, extract the logger from the optimization result:
reader = res.logger
Alternatively, you can create the reader like this:
reader = om.SQLiteLogReader("my_log.db")
Read the start params
reader.read_start_params()
array([0, 1, 2, 3, 4])
Read a specific iteration (use -1 for the last)
reader.read_iteration(-1)
IterationStateWithId(params=array([ 0.00000000e+00, -2.19792136e-07, -4.01986529e-08, -1.26862247e-07,
-2.06263028e-07]), timestamp=2010.070966616, scalar_fun=1.08562981500731e-13, valid=True, raw_fun={'value': np.float64(1.08562981500731e-13), 'info': None}, step=1, exceptions=None, rowid=3)
Read the full history
reader.read_history().keys()
dict_keys(['params', 'fun', 'time'])
Plot the history from a log#
fig = om.criterion_plot("my_log.db")
fig.show(renderer="png")
---------------------------------------------------------------------------
FileNotFoundError Traceback (most recent call last)
Cell In[11], line 1
----> 1 fig = om.criterion_plot("my_log.db")
2 fig.show(renderer="png")
File ~/checkouts/readthedocs.org/user_builds/estimagic/conda/stable/lib/python3.10/site-packages/optimagic/visualization/history_plots.py:75, in criterion_plot(results, names, max_evaluations, template, palette, stack_multistart, monotone, show_exploration)
71 _data = _extract_plotting_data_from_results_object(
72 res, stack_multistart, show_exploration, plot_name="criterion_plot"
73 )
74 elif isinstance(res, (str, Path)):
---> 75 _data = _extract_plotting_data_from_database(
76 res, stack_multistart, show_exploration
77 )
78 else:
79 msg = "results must be (or contain) an OptimizeResult or a path to a log"
File ~/checkouts/readthedocs.org/user_builds/estimagic/conda/stable/lib/python3.10/site-packages/optimagic/visualization/history_plots.py:385, in _extract_plotting_data_from_database(res, stack_multistart, show_exploration)
363 def _extract_plotting_data_from_database(res, stack_multistart, show_exploration):
364 """Extract data for plotting from database.
365
366 Args:
(...)
383
384 """
--> 385 reader = LogReader.from_options(SQLiteLogOptions(res))
386 _problem_table = reader.problem_df
388 direction = _problem_table["direction"].tolist()[-1]
File ~/checkouts/readthedocs.org/user_builds/estimagic/conda/stable/lib/python3.10/site-packages/optimagic/logging/logger.py:95, in LogReader.from_options(cls, log_options)
88 if log_reader_class is None:
89 raise ValueError(
90 f"No LogReader implementation found for type "
91 f"{type(log_options)}. Available option types: "
92 f"\n {list(_LOG_OPTION_LOG_READER_REGISTRY.keys())}"
93 )
---> 95 return log_reader_class._create(log_options)
File ~/checkouts/readthedocs.org/user_builds/estimagic/conda/stable/lib/python3.10/site-packages/optimagic/logging/logger.py:470, in SQLiteLogReader._create(cls, log_options)
458 @classmethod
459 def _create(cls, log_options: SQLiteLogOptions) -> SQLiteLogReader:
460 """Create an instance of SQLiteLogReader using the provided log options.
461
462 Args:
(...)
468
469 """
--> 470 return cls(log_options.path)
File ~/checkouts/readthedocs.org/user_builds/estimagic/conda/stable/lib/python3.10/site-packages/optimagic/logging/logger.py:449, in SQLiteLogReader.__init__(self, path)
447 def __init__(self, path: str | Path):
448 if not os.path.exists(path):
--> 449 raise FileNotFoundError(f"No file found at {path=}")
451 log_options = SQLiteLogOptions(
452 path, fast_logging=True, if_database_exists=ExistenceStrategy.EXTEND
453 )
454 self._iteration_store = IterationStore(log_options)
FileNotFoundError: No file found at path='m'
fig = om.params_plot("my_log.db", selector=lambda x: x[1:3])
fig.show(renderer="png")