A "logger" is a function that reports the progress of a program, usually to a file. Loggers are used in GEMS to report on the progress of the evolutionary algorithm.

The logger is called by the GP algorithm every time it has constructed a new population: the logger is passed the generation number and an array holding the entire population of individuals.

Two kinds of log are built in:

  1. a "short" log reports on the best performing individual in the population, and adds a single line of data to a CSV file.

  2. a "trace" log reports in detail on all individuals in the population, recording information on the entire population into a file.

Custom log reports can be created, by providing your own reporting function.

Finally, although by default the logger is called for every generation, a filter can be added to specify the generation numbers on which to call the logger.

Examples are given below.

Functions

next combine-loggers (logger-1 logger-2)

Creates a combined logger, which calls each given logger in turn.

example A typical situation requiring more than one logger is to both output a short log for every generation, and additionally a trace output of the last generation, to analyse the final set of models.

(gems:combine-loggers
  (gems:make-logger "log-file.csv")            ; 1
  (gems:make-logger "final-population.yml"     ; 2
                    :kind :trace
                    :filter #'(lambda (generation-number)
                                 (= generation-number *max-generations*))))
1 A short log, will output information for every generation to csv file.
2 The trace output, outputs detailed information for generation max-generations only.

example Example:

> sbcl
* (require 'asdf)
* (require 'gems)
* (gems:dated-log-name)
"log-20210311-121753.csv"

next make-logger (filename &key name kind filter)

This function is used to create the logger function, to pass to the GP system. Based on the value passed to :kind, three kinds of log file can be created:

Log files

If the logger is created with :kind :log, or without a :kind at all, then a default short log will be created. This default log outputs, in CSV format: generation-number, fitness, and any values found in gp:individual-extras. An optional :header list can be given, to give a header of meaningful names to the CSV file - this is useful when importing the results into another program for processing.

example Example:

Using the log function:

(gems:make-logger "log-file.csv")

Information will appear in the file "log-file.csv". If you return the program size in extras from your evaluation function, the output would have three columns: generation-number, fitness, program-size.

A sample of the file contents:

70, 0.93, 30.00
71, 0.44, 34.00
72, 0.44, 35.00
73, 0.45, 45.00
74, 0.45, 45.00
75, 0.43, 40.00
Trace files

If the logger is created with :kind :trace, then a detailed trace file will be created. This trace file outputs every individual of the given population, with its fitness and program.

example Example:

(gems:make-logger "trace-output.yml"
                  :name "DMTS"
                  :kind :trace)

Would output a detailed trace for every generation into the file "trace-output.yml" in the following format:

---
name: DMTS
---
generation:
  number: 1
  individuals:
    - fitness: 0.9699
      extras: (0.50 0.48 735.00 0.02 21.00 0.10)
      program: |
               (PUT-STM)
    - fitness: 0.9699
      extras: (0.92 0.04 2800.83 0.87 59.00 0.29)
      program: |
               (COMPARE-2-3)
...skipping
                   (PUT-STM))
---
generation:
  number: 2
  individuals:
    - fitness: 0.9699
...etc

(notice use of the "name" value - "DMTS")

Trace files can be read back in using read-trace.

Note Currently, the trace file is saved in YAML format. This is useful for developing external tools to work with the trace file, but should be treated as "subject to change". Currently, the trace files can become very large for some simulations, and a more efficient format may be developed in future.
Functions

A custom log function may also be provided to :kind, accepting three arguments (str generation-number population)

str

the stream (file) to send the output to

generation-number

the current generation number (starting from 1)

population

an array of the current population.

The function can output any information about the population that it chooses, in any required format.

Tip str can be ignored, and the function used to update another resource, such as a graph.
Filter

The filter is a function to control which generations are reported on. By default the logger will be called on every population.

A typical use-case is to get a trace (all details on every member of the population) for only the final generation, which can be achieved by checking if the given generation number equals the largest we can have.

#'(lambda (gen-number) (= gen-number *max-generations*))