Documentation - xcsf-dev/xcsf GitHub Wiki

Directory Structure

  • build empty folder to be used for building executables
  • cfg contains configuration files for initialising parameters for the stand-alone executable
  • doc contains files for generating Doxygen documentation
  • env contains data files for problem environments
  • lib contains third-party libraries for random number generation, unit testing, pybind, etc.
  • python contains example Python scripts
  • python/notebooks contains jupyter notebook examples
  • test contains unit tests
  • xcsf contains XCSF source code
  • xcsf/utils contains Python utilities for visualisation, etc.

Doxygen + graphviz

To build locally see Compiling and Running.

The github-pages contains prebuilt documentation, including:

Parameters

The main data structure XCSF located within xcsf.h is passed to almost all functions, enabling access to parameters. This structure directly contains general parameters, e.g., double BETA; which can be accessed with xcsf->BETA. It also contains sub-structures containing groups of parameters, e.g., for the EA with struct ArgsEA *ea;

The sub-structures containing groups of parameters are:

  • Condition parameters defined within struct ArgsCond located within condition.h.
  • Action parameters defined within struct ArgsAct located within action.h.
  • Prediction parameters defined within struct ArgsPred located within prediction.h.
  • EA parameters defined within struct ArgsEA located within ea.h.

Every parameter needs the following tasks implemented:

  1. setting the parameter value - this provides a single point at which the values change and minimums and maximums can be defined to help users and avoid undefined behaviour;
  2. setting the default parameter value;
  3. exporting the parameter as a JSON formatted string;
  4. importing the parameter with a JSON string;
  5. printing the parameter value (using the JSON export function);
  6. saving the parameter value to persistent storage;
  7. loading the parameter value from persistent storage;

These functions for general parameters are defined within param.c. For the above-mentioned sub-structures, the functions are located within the respective *.c files. These can then be accessed through the XCSF structure, e.g., xcsf->cond->bits.

Finally, to enable the parameters to be set by Python, they may also need to be added to pybind_wrapper.cpp.

Scikit-learn

The Python scikit-learn package requires the memory address of parameter values to match between calls to set_params() and get_params() so to facilitate this the following approach has been taken within pybind_wrapper.cpp. This approach has the benefit that both the stand-alone executable and the Python library use the same JSON parsing functions to set parameters and parameters do not have to be individually declared in the Python library constructor. Any future language interfaces (such as for Julia) would also benefit from this.

def __init__(**kwargs) -> None: ...

  1. Constructor sets parameter values to defaults internally, including x_dim=1, y_dim=1, n_actions=1;
  2. These internal parameters are exported via JSON and used to update an exposed params dict;
  3. If any kwargs are specified, set_params() is automatically called (see below);

def set_params(**kwargs) -> xcsf.XCS: ...

  1. kwargs are imported internally via JSON;
  2. params dict is updated directly with the kwargs (no JSON export);

def get_params() -> dict: ...

  1. Returns the exposed params dict;

def internal_params() -> dict: ...

  1. Returns the exported internal parameters via JSON as a dict;

Extending Classifiers

Despite being in C, classifiers are structured in an OO style via the use of virtual functions.

The classifier data structure Cl is located in xcsf.h and contains pointers to both data structures and virtual method tables (which hold pointers to the functions that operate on those data structures) for conditions, actions, and predictions. For conditions, the virtual table that defines the interface is located in condition.h as CondVtbl and all implementations of conditions must implement these functions. Switching between different condition implementations via hyperparameters is performed in condition.c. Similar functionality for actions is performed in action.h and action.c, likewise for predictions in prediction.h and prediction.c.

Functions operating on individual classifiers are implemented in cl.c and these functions can therefore call the abstract functions. For example, cond_match() will invoke an implementation such as cond_rectangle_match() or cond_neural_match() depending on which has been specified through the hyperparameters.