class documentation

class Logger(object):

View In Hierarchy

log an arbitrary number of data (a data row) per "timestep".

The add method can be called several times per timestep, the push method must be called once per timestep. Callables are called in the push method and their output is logged. push finally also dumps the current data row to disk. load reads all logged data in and, like plot, will only work if the same number of data was pushed each and every time.

To-be-logged "values" can be scalars or iterables (like lists or nparrays).

The current data is saved to a file and cleared after each timestep. The name and filename attributes are based on either the name or the logged instance class as given as first argument. Only if a name was given, push overwrites the derived file if it exists.

To append data, set self.counter > 0 or call load before to call push the first time (make sure that the _name attribute has the desired value either way). len(self.load().data) is the number of current data.

A minimal practical example logging some nicely transformed attribute values of an object (looping over Logger and LoggerDummy for testing purpose only):

>>> import numpy as np
>>> import cma
>>> for Logger in [cma.logger.Logger, cma.logger.LoggerDummy]:
...     es = cma.CMAEvolutionStrategy(3 * [1], 2, dict(maxiter=9, verbose=-9))
...     lg = Logger(es,  # es-instance serves as argument to callables and for attribute access
...                 callables=[lambda s: s.best.f,
...                            lambda s: np.log10(np.abs(s.best.f)),
...                            lambda s: np.log10(s.sigma),
...                           ],
...                 labels=['best f', 'lg(best f)', r'lg($\sigma$)'])
...     _ = es.optimize(cma.ff.sphere, callback=lg.push)
...     # lg.plot()  # caveat: requires matplotlib and clears current figure like gcf().clear()
...     lg2 = Logger(lg.name).load()  # same logger without callables assigned
...     lg3 = Logger(lg.filename).load()  # ditto
...     assert len(lg.load().data) == lg.count == 9 or isinstance(lg, cma.logger.LoggerDummy)
...     assert np.all(lg.data == lg2.data) and np.all(lg.data == lg3.data)
...     assert lg.labels == lg2.labels
...     lg.delete()  # delete data file, logger can still be (re-)used for new data
Method __call__ see also method push.
Method __del__ Undocumented
Method __init__ obj_or_name is the instance that we want to observe,
Method add data may be a value, or a list, or a numpy array.
Method delete delete current data file and reset count to zero
Method load Undocumented
Method plot plot logged data using the plot function.
Method push call stack() and finalize the current timestep, ignore input arguments.
Method push_header Undocumented
Class Variable extension Undocumented
Class Variable fields_read names of attributes written to and read from file
Instance Variable attributes Undocumented
Instance Variable callables Undocumented
Instance Variable count Undocumented
Instance Variable current_data Undocumented
Instance Variable data Undocumented
Instance Variable format Undocumented
Instance Variable labels Undocumented
Instance Variable name Undocumented
Instance Variable obj Undocumented
Instance Variable path Undocumented
Instance Variable plot_transformations Undocumented
Property filename full filename as absolute path (stored in attribute _name)
Method _add_defaults add data from registered attributes and callables, called by push
Method _autoname set name and _name attributes.
Method _compose_name add unique_id to name before ".logdata"
Method _create_path return absolute path or '' if not name_prefix
Method _stack stack data into current row managing the different access...
Method _unique_name_addition return id:str that makes name or self._name unique
Instance Variable _delete Undocumented
Instance Variable _name Undocumented
def __call__(self, obj=None):

see also method push.

TODO: replacing obj here is somewhat inconsistent, but maybe an effective hack.

def __del__(self):

Undocumented

def __init__(self, obj_or_name, attributes=None, callables=None, path='outcmaes/', name=None, labels=None, delete=False, plot_transformations=None):

obj_or_name is the instance that we want to observe,

or a name, or an absolute path to a file.

attributes are attribute names[:str] of obj_or_name, however

callables are more general in their usage and hence recommended. They allow attribute access and transformations, for example like lambda es: np.log10(sum(es.mean**2)**0.5 / es.sigma).

When a callable accepts an argument, it is called with obj_or_name as argument. The returned value of callables and the current values of attributes are logged each time when push is called.

Details: path is not used if obj_or_name is an absolute path name, e.g. the filename attribute from another logger. If delete is True, the data file is deleted when the instance is destructed. This can also be controlled or prevented by setting the boolean _delete attribute.

def add(self, data):

data may be a value, or a list, or a numpy array.

See also push to complete the iteration.

def delete(self):

delete current data file and reset count to zero

def load(self):

Undocumented

def plot(self, plot=None, clear=True, transformations=None):

plot logged data using the plot function.

If clear, this calls matplotlib.pyplot.gca().clear() before to plot in the current figure. The default value of clear may change in future.

If transformations[i] is a callable it is used to transform the i-th data column like i_th_column = transformations[i](data[:,i]).

def push(self, *args):

call stack() and finalize the current timestep, ignore input arguments.

def push_header(self):

Undocumented

extension: str =

Undocumented

fields_read: list[str] =

names of attributes written to and read from file

attributes =

Undocumented

callables =

Undocumented

count =

Undocumented

current_data: list =

Undocumented

data =

Undocumented

format: str =

Undocumented

labels =

Undocumented

name =

Undocumented

obj =

Undocumented

path =

Undocumented

plot_transformations =

Undocumented

@property
filename =

full filename as absolute path (stored in attribute _name)

def _add_defaults(self):

add data from registered attributes and callables, called by push

def _autoname(self, obj):

set name and _name attributes.

Loggers based on the same class are separted by calling _unique_name_addition afterwards.

def _compose_name(self, name, unique_id):

add unique_id to name before ".logdata"

def _create_path(self, name_prefix=None):

return absolute path or '' if not name_prefix

def _stack(self, data):

stack data into current row managing the different access...

...and type formats.

def _unique_name_addition(self, name=None):

return id:str that makes name or self._name unique

_delete =

Undocumented

_name =

Undocumented