34 #include <pybind11/numpy.h>
35 #include <pybind11/pybind11.h>
36 #include <pybind11/stl.h>
41 namespace py = pybind11;
97 explicit XCS(py::kwargs kwargs)
225 fit(
const py::array_t<double> input,
const int action,
const double reward)
227 py::buffer_info buf = input.request();
229 std::ostringstream
error;
230 error <<
"fit(): x_dim is not equal to: " <<
xcs.
x_dim << std::endl;
231 throw std::invalid_argument(
error.str());
234 std::ostringstream
error;
237 throw std::invalid_argument(
error.str());
239 state = (
double *) buf.ptr;
287 decision(
const py::array_t<double> input,
const bool explore)
289 py::buffer_info buf = input.request();
291 std::ostringstream
error;
293 throw std::invalid_argument(
error.str());
295 state = (
double *) buf.ptr;
308 update(
const double reward,
const bool done)
322 error(
const double reward,
const bool done,
const double max_p)
338 const py::array_t<double> Y)
341 const py::buffer_info buf_x = X.request();
342 const py::buffer_info buf_y = Y.request();
344 if (buf_x.ndim < 1 || buf_x.ndim > 2) {
345 throw std::invalid_argument(
"X must be 1 or 2-D array");
347 if (buf_y.ndim < 1 || buf_y.ndim > 2) {
348 throw std::invalid_argument(
"Y must be 1 or 2-D array");
350 if (buf_x.shape[0] != buf_y.shape[0]) {
351 throw std::invalid_argument(
"X and Y n_samples are not equal");
353 if (buf_x.ndim > 1 && buf_x.shape[1] !=
xcs.
x_dim) {
354 std::ostringstream
error;
355 error <<
"load_input():";
356 error <<
" received x_dim: (" << buf_x.shape[1] <<
")";
358 error <<
"Perhaps reshape your data.";
359 throw std::invalid_argument(
error.str());
361 if (buf_y.ndim > 1 && buf_y.shape[1] !=
xcs.
y_dim) {
362 std::ostringstream
error;
363 error <<
"load_input():";
364 error <<
" received y_dim: (" << buf_y.shape[1] <<
")";
366 error <<
"Perhaps reshape your data.";
367 throw std::invalid_argument(
error.str());
373 data->
x =
static_cast<double *
>(buf_x.ptr);
374 data->
y =
static_cast<double *
>(buf_y.ptr);
388 std::ostringstream status;
390 status <<
" trials=" << trial;
391 status <<
" train=" << std::fixed << std::setprecision(5) << train;
394 status <<
" val=" << std::fixed << std::setprecision(5) << val;
396 status <<
" pset=" << std::fixed << std::setprecision(1) << psize;
397 status <<
" mset=" << std::fixed << std::setprecision(1) << msize;
398 status <<
" mfrac=" << std::fixed << std::setprecision(2) << mfrac;
399 py::print(status.str());
429 if (kwargs.contains(
"validation_data")) {
430 py::tuple data = kwargs[
"validation_data"].cast<py::tuple>();
432 if (data.size() != 2) {
433 throw std::invalid_argument(
434 "validation_data must be a tuple with two arrays");
436 py::array_t<double> X_val = data[0].cast<py::array_t<double>>();
437 py::array_t<double> y_val = data[1].cast<py::array_t<double>>();
454 bool terminate =
false;
456 for (py::handle item : callbacks) {
457 if (py::isinstance<Callback>(item)) {
458 Callback *cb = py::cast<Callback *>(item);
459 if (cb->
run(&
xcs, metrics)) {
463 throw std::invalid_argument(
"unsupported callback");
476 for (py::handle item : callbacks) {
477 if (py::isinstance<Callback>(item)) {
478 Callback *cb = py::cast<Callback *>(item);
481 throw std::invalid_argument(
"unsupported callback");
499 fit(
const py::array_t<double> X_train,
const py::array_t<double> y_train,
500 const bool shuffle,
const bool warm_start,
const bool verbose,
501 py::object callbacks, py::kwargs kwargs)
511 if (py::isinstance<py::list>(callbacks)) {
512 calls = callbacks.cast<py::list>();
517 for (
int i = 0; i < n; ++i) {
518 const int start = i * n_trials;
521 double val_error = 0;
545 const py::buffer_info buf_c = cover.request();
546 if (buf_c.ndim != 1) {
547 std::ostringstream err;
548 err <<
"cover must be an array of shape (1, " <<
xcs.
y_dim <<
")"
550 throw std::invalid_argument(err.str());
553 std::ostringstream err;
554 err <<
"cover length = " << buf_c.shape[0] <<
" but expected "
556 throw std::invalid_argument(err.str());
558 return reinterpret_cast<double *
>(buf_c.ptr);
568 if (cover.is_none()) {
571 py::array_t<double> cover_arr = cover.cast<py::array_t<double>>();
584 predict(
const py::array_t<double> X,
const py::object &cover)
586 const py::buffer_info buf_x = X.request();
587 if (buf_x.ndim < 1 || buf_x.ndim > 2) {
588 throw std::invalid_argument(
"predict(): X must be 1 or 2-D array");
590 if (buf_x.ndim > 1 && buf_x.shape[1] !=
xcs.
x_dim) {
591 std::ostringstream
error;
592 error <<
"predict():";
593 error <<
" received x_dim: (" << buf_x.shape[1] <<
")";
595 error <<
"Perhaps reshape your data.";
596 throw std::invalid_argument(
error.str());
599 const double *input =
reinterpret_cast<double *
>(buf_x.ptr);
604 return py::array_t<double>(
618 score(
const py::array_t<double> X,
const py::array_t<double> Y,
const int N,
619 const py::object &cover)
638 const char *filename =
"_tmp_pickle.bin";
641 std::ifstream file(filename, std::ios::binary);
642 std::string
state((std::istreambuf_iterator<char>(file)),
643 std::istreambuf_iterator<char>());
646 if (std::remove(filename) != 0) {
647 perror(
"Error deleting temporary pickle file");
650 return py::bytes(
state);
662 const char *filename =
"_tmp_pickle.bin";
663 std::ofstream file(filename, std::ios::binary);
664 file.write(
state.cast<std::string>().c_str(),
665 state.cast<std::string>().size());
674 if (std::remove(filename) != 0) {
675 perror(
"Error deleting temporary pickle file");
822 py::module json = py::module::import(
"json");
823 py::object parsed_json = json.attr(
"loads")(json_str);
824 py::dict result(parsed_json);
827 if (
params.contains(
"random_state")) {
828 py::object rs =
params[
"random_state"];
829 if (py::isinstance<py::int_>(rs) && py::int_(rs) < 0) {
830 params[
"random_state"] = py::none();
856 py::dict kwargs_dict(kwargs);
858 for (
const auto &item : kwargs_dict) {
859 params[item.first] = item.second;
862 if (kwargs_dict.contains(
"random_state")) {
863 py::object rs = kwargs[
"random_state"];
865 kwargs_dict[
"random_state"] = -1;
869 py::module json_module = py::module::import(
"json");
870 py::object json_dumps = json_module.attr(
"dumps")(kwargs_dict);
871 std::string json_str = json_dumps.cast<std::string>();
872 const char *json_params = json_str.c_str();
885 py::module json_module = py::module::import(
"json");
898 cJSON *json = cJSON_Parse(json_str.c_str());
921 std::ofstream outfile(filename);
933 std::ifstream infile(filename);
934 std::stringstream buffer;
935 buffer << infile.rdbuf();
942 m.doc() =
"XCSF learning classifier: rule-based online evolutionary "
943 "machine learning.\nFor details on how to use this module see: "
944 "https://github.com/xcsf-dev/xcsf/wiki/Python-Library-Usage";
946 double (
XCS::*fit1)(
const py::array_t<double>,
const int,
const double) =
948 XCS &(
XCS::*fit2)(
const py::array_t<double>,
const py::array_t<double>,
949 const bool,
const bool,
const bool, py::object,
953 double (
XCS::*error2)(
const double,
const bool,
const double) = &
XCS::error;
955 py::class_<Callback, std::unique_ptr<Callback, py::nodelete>>(m,
959 std::unique_ptr<EarlyStoppingCallback, py::nodelete>>(
960 m,
"EarlyStoppingCallback")
961 .def(py::init<py::str, int, bool, double, int, bool>(),
962 "Creates a callback for terminating the fit function early.",
963 py::arg(
"monitor") =
"train", py::arg(
"patience") = 0,
964 py::arg(
"restore_best") =
false, py::arg(
"min_delta") = 0,
965 py::arg(
"start_from") = 0, py::arg(
"verbose") =
true);
968 std::unique_ptr<CheckpointCallback, py::nodelete>>(
969 m,
"CheckpointCallback")
970 .def(py::init<py::str, std::string, bool, int, bool>(),
971 "Creates a callback for automatically saving XCSF.",
972 py::arg(
"monitor") =
"train", py::arg(
"filename") =
"xcsf.bin",
973 py::arg(
"save_best_only") =
false, py::arg(
"save_freq") = 0,
974 py::arg(
"verbose") =
true);
976 py::class_<XCS>(m,
"XCS")
977 .def(py::init(),
"Creates a new XCSF class with default arguments.")
978 .def(py::init<py::kwargs>(),
979 "Creates a new XCSF class with specified arguments.")
981 "Creates/updates an action set for a given (state, action, "
982 "reward). state shape must be: (x_dim, ).",
983 py::arg(
"state"), py::arg(
"action"), py::arg(
"reward"))
985 "Executes MAX_TRIALS number of XCSF learning iterations using the "
986 "provided training data. X_train shape must be: (n_samples, "
987 "x_dim). y_train shape must be: (n_samples, y_dim).",
988 py::arg(
"X_train"), py::arg(
"y_train"), py::arg(
"shuffle") =
true,
989 py::arg(
"warm_start") =
false, py::arg(
"verbose") =
true,
990 py::arg(
"callbacks") = py::none())
993 "Returns the error using at most N random samples from the "
994 "provided data. N=0 uses all. X shape must be: (n_samples, x_dim). "
995 "y shape must be: (n_samples, y_dim). If the match set is empty "
996 "for a sample, the value of the cover array will be used "
998 py::arg(
"X"), py::arg(
"y"), py::arg(
"N") = 0,
999 py::arg(
"cover") = py::none())
1000 .def(
"error", error1,
1001 "Returns a moving average of the system error, updated with step "
1003 .def(
"error", error2,
1004 "Returns the reinforcement learning system prediction error.",
1005 py::arg(
"reward"), py::arg(
"done"), py::arg(
"max_p"))
1007 "Returns the XCSF prediction array for the provided input. X "
1008 "shape must be: (n_samples, x_dim). Returns an array of shape: "
1009 "(n_samples, y_dim). If the match set is empty for a sample, the "
1010 "value of the cover array will be used, otherwise zeros. "
1011 "Cover must be an array of shape: y_dim.",
1012 py::arg(
"X"), py::arg(
"cover") = py::none())
1014 "Saves the current state of XCSF to persistent storage.",
1015 py::arg(
"filename"))
1017 "Loads the current state of XCSF from persistent storage.",
1018 py::arg(
"filename"))
1020 "Stores the current XCSF population in memory for later "
1021 "retrieval, overwriting any previously stored population.")
1023 "Retrieves the previously stored XCSF population from memory.")
1024 .def(
"init_trial", &
XCS::init_trial,
"Initialises a multi-step trial.")
1027 "Initialises a step in a multi-step trial.")
1028 .def(
"end_step", &
XCS::end_step,
"Ends a step in a multi-step trial.")
1030 "Constructs the match set and selects an action to perform for "
1031 "reinforcement learning. state shape must be: (x_dim, )",
1032 py::arg(
"state"), py::arg(
"explore"))
1034 "Creates the action set using the previously selected action.",
1035 py::arg(
"reward"), py::arg(
"done"))
1036 .def(
"time", &
XCS::get_time,
"Returns the current EA time.")
1038 "Returns a dictionary of performance metrics.")
1040 "Returns the number of macro-classifiers in the population.")
1042 "Returns the number of micro-classifiers in the population.")
1044 "Returns the average condition size of classifiers in the "
1047 "Returns the average prediction size of classifiers in the "
1050 "Returns the mean eta for a prediction layer.", py::arg(
"layer"))
1052 "Returns the mean number of neurons for a prediction layer.",
1055 "Returns the mean number of layers in the prediction networks.")
1057 "Returns the mean number of connections for a prediction layer.",
1060 "Returns the mean number of neurons for a condition layer.",
1063 "Returns the mean number of layers in the condition networks.")
1065 "Returns the mean number of connections for a condition layer.",
1068 "Returns the average match set size.")
1070 "Returns the average action set size.")
1072 "Returns the mean fraction of inputs matched by the best rule.")
1073 .def(
"print_pset", &
XCS::print_pset,
"Prints the current population.",
1074 py::arg(
"condition") =
true, py::arg(
"action") =
true,
1075 py::arg(
"prediction") =
true)
1077 "Prints the XCSF parameters and their current values.")
1079 "Inserts a new hidden layer before the output layer within all "
1080 "prediction neural networks in the population.")
1082 "Switches from autoencoding to classification.", py::arg(
"y_dim"),
1085 "Returns a JSON formatted string representing the population set.",
1086 py::arg(
"condition") =
true, py::arg(
"action") =
true,
1087 py::arg(
"prediction") =
true)
1089 "Writes the current population set to a file in JSON.",
1090 py::arg(
"filename"))
1092 "Reads classifiers from a JSON file and adds to the population.",
1093 py::arg(
"filename"))
1095 "Returns a dictionary of parameters and their values.")
1098 "Creates a classifier from JSON and inserts into the population.",
1099 py::arg(
"json_str"))
1101 "Creates classifiers from JSON and inserts into the population.",
1102 py::arg(
"json_str"))
Interface for classifier actions.
virtual void finish(struct XCSF *xcsf)=0
virtual bool run(struct XCSF *xcsf, py::dict metrics)=0
Callback to save XCSF at some frequency.
Callback to stop training when a certain metric has stopped improving.
Python XCSF class data structure.
double get_pset_mean_cond_size(void)
XCS & fit(const py::array_t< double > X_train, const py::array_t< double > y_train, const bool shuffle, const bool warm_start, const bool verbose, py::object callbacks, py::kwargs kwargs)
Executes MAX_TRIALS number of XCSF learning iterations using the provided training data.
double get_pset_mean_cond_connections(const int layer)
double get_pset_mean_cond_neurons(const int layer)
void store(void)
Stores the current population in memory for later retrieval.
double get_mset_size(void)
void end_trial(void)
Frees memory used by a reinforcement learning trial.
size_t load(const char *filename)
Reads the entire current state of XCSF from a file.
void json_read(const std::string &filename)
Reads classifiers from a JSON file and adds to the population.
double score(const py::array_t< double > X, const py::array_t< double > Y, const int N, const py::object &cover)
Returns the error using N random samples from the provided data.
void reset(void)
Resets basic constructor variables.
struct XCSF xcs
XCSF data structure.
py::dict internal_params()
Returns a dictionary of the internal parameters.
double get_aset_size(void)
struct Input * test_data
Test data for supervised learning.
double get_pset_mean_pred_eta(const int layer)
void json_write(const std::string &filename)
Writes the current population set to a file in JSON.
void callbacks_finish(py::list callbacks)
Executes callback finish.
XCS(py::kwargs kwargs)
Constructor.
void json_insert_cl(const std::string &json_str)
Creates a classifier from JSON and inserts into the population.
double * state
Current input state for RL.
struct Input * train_data
Training data for supervised learning.
double get_pset_mean_cond_layers(void)
double get_pset_mean_pred_connections(const int layer)
double payoff
Current reward for RL.
void load_input(struct Input *data, const py::array_t< double > X, const py::array_t< double > Y)
Loads an input data structure for fitting.
static XCS deserialize(const py::bytes &state)
Implements pickle file reading.
void print_params(void)
Prints the XCSF parameters and their current values.
size_t save(const char *filename)
Writes the entire current state of XCSF to a file.
double get_pset_mean_pred_size(void)
double error(const double reward, const bool done, const double max_p)
Returns the reinforcement learning system prediction error.
XCS()
Default Constructor.
struct Input * val_data
Validation data.
double * get_cover(const py::array_t< double > cover)
Returns the values specified in the cover array.
void update_metrics(const double train, const double val, const int n_trials)
Updates performance metrics.
py::array_t< double > predict(const py::array_t< double > X, const py::object &cover)
Returns the XCSF prediction array for the provided input.
void print_pset(const bool condition, const bool action, const bool prediction)
Prints the current population.
void pred_expand(void)
Inserts a new hidden layer before the output layer within all prediction neural networks in the popul...
void init_step(void)
Initialises a step in a reinforcement learning trial.
void end_step(void)
Ends a step in a reinforcement learning trial.
int action
Current action for RL.
void load_validation_data(py::kwargs kwargs)
Loads validation data if present in kwargs.
void print_status()
Prints the current performance metrics.
double fit(const py::array_t< double > input, const int action, const double reward)
Creates/updates an action set for a given (state, action, reward).
py::bytes serialize() const
Implements pickle file writing.
int decision(const py::array_t< double > input, const bool explore)
Selects an action to perform in a reinforcement learning problem.
void set_cover(const py::object &cover)
Sets the XCSF cover array to values given, or zeros.
py::dict get_metrics(void)
void ae_to_classifier(const int y_dim, const int n_del)
Switches from autoencoding to classification.
XCS & set_params(py::kwargs kwargs)
Sets parameter values.
void init_trial(void)
Initialises a reinforcement learning trial.
bool callbacks_run(py::list callbacks)
Executes callbacks and returns whether to terminate.
const char * json_export(const bool condition, const bool action, const bool prediction)
Returns a JSON formatted string representing the population set.
void update_params()
Updates the Python object's parameter dictionary.
py::dict params
Dictionary of parameters and their values.
double get_pset_mean_pred_neurons(const int layer)
double error(void)
Returns the current system error.
void retrieve(void)
Retrieves the stored population, setting it as current.
double get_pset_mean_pred_layers(void)
py::dict get_params(const bool deep)
Returns a dictionary of parameters.
void json_insert(const std::string &json_str)
Creates classifiers from JSON and inserts into the population.
void update(const double reward, const bool done)
Creates the action set using the previously selected action, updates the classifiers,...
double clset_mean_pred_size(const struct XCSF *xcsf, const struct Set *set)
Calculates the mean prediction size of classifiers in the set.
double clset_mean_cond_size(const struct XCSF *xcsf, const struct Set *set)
Calculates the mean condition size of classifiers in the set.
void clset_json_insert(struct XCSF *xcsf, const char *json_str)
Creates classifiers from JSON and inserts into the population.
void clset_json_insert_cl(struct XCSF *xcsf, const cJSON *json)
Creates a classifier from cJSON and inserts in the population set.
char * clset_json_export(const struct XCSF *xcsf, const struct Set *set, const bool return_cond, const bool return_act, const bool return_pred)
Returns a json formatted string representation of a classifier set.
Functions operating on sets of classifiers.
double clset_mean_cond_neurons(const struct XCSF *xcsf, const struct Set *set, const int layer)
Calculates the mean number of condition neurons for a given layer.
double clset_mean_pred_eta(const struct XCSF *xcsf, const struct Set *set, const int layer)
Calculates the mean prediction layer ETA of classifiers in the set.
double clset_mean_cond_layers(const struct XCSF *xcsf, const struct Set *set)
Calculates the mean number of condition layers in the set.
double clset_mean_pred_connections(const struct XCSF *xcsf, const struct Set *set, const int layer)
Calculates the mean number of prediction connections in the set.
double clset_mean_pred_layers(const struct XCSF *xcsf, const struct Set *set)
Calculates the mean number of prediction layers in the set.
double clset_mean_cond_connections(const struct XCSF *xcsf, const struct Set *set, const int layer)
Calculates the mean number of condition connections in the set.
double clset_mean_pred_neurons(const struct XCSF *xcsf, const struct Set *set, const int layer)
Calculates the mean number of prediction neurons for a given layer.
Functions operating on sets of neural classifiers.
Interface for classifier conditions.
Evolutionary algorithm functions.
void param_json_import(struct XCSF *xcsf, const char *json_str)
Sets the parameters from a json formatted string.
void param_print(const struct XCSF *xcsf)
Prints all XCSF parameters.
const char * param_set_explore(struct XCSF *xcsf, const bool a)
char * param_json_export(const struct XCSF *xcsf)
Returns a json formatted string representation of the parameters.
void param_init(struct XCSF *xcsf, const int x_dim, const int y_dim, const int n_actions)
Initialises default XCSF parameters.
Functions for setting and printing parameters.
Interface for classifier predictions.
Checkpoint callback for Python library.
Early stopping callback for Python library.
Utilities for Python library.
std::string get_timestamp()
Returns a formatted string for displaying time.
int size
Number of macro-classifiers.
struct Clist * list
Linked list of classifiers.
int num
The total numerosity of classifiers.
double mset_size
Average match set size.
int x_dim
Number of problem input variables.
int PERF_TRIALS
Number of problem instances to avg performance output.
double aset_size
Average action set size.
int pa_size
Prediction array size.
int n_actions
Number of class labels / actions.
struct Set pset
Population set.
int MAX_TRIALS
Number of problem instances to run in one experiment.
double * cover
Values to return for a prediction instead of covering.
double error
Average system error.
int time
Current number of EA executions.
double mfrac
Generalisation measure.
int y_dim
Number of problem output variables.
void utils_json_parse_check(const cJSON *json)
Checks whether JSON parsed correctly.
Utility functions for random number handling, etc.
double xcs_rl_fit(struct XCSF *xcsf, const double *state, const int action, const double reward)
Creates and updates an action set for a given (state, action, reward).
void xcs_rl_update(struct XCSF *xcsf, const double *state, const int action, const double reward, const bool done)
Provides reinforcement to the sets.
int xcs_rl_decision(struct XCSF *xcsf, const double *state)
Selects an action to perform in a reinforcement learning problem.
void xcs_rl_init_step(struct XCSF *xcsf)
Initialises a step in a reinforcement learning trial.
void xcs_rl_end_trial(struct XCSF *xcsf)
Frees memory used by a reinforcement learning trial.
double xcs_rl_error(struct XCSF *xcsf, const int action, const double reward, const bool done, const double max_p)
Returns the reinforcement learning system prediction error.
void xcs_rl_init_trial(struct XCSF *xcsf)
Initialises a reinforcement learning trial.
void xcs_rl_end_step(struct XCSF *xcsf, const double *state, const int action, const double reward)
Ends a step in a reinforcement learning trial.
Reinforcement learning functions.
double xcs_supervised_fit(struct XCSF *xcsf, const struct Input *train_data, const struct Input *test_data, const bool shuffle, const int start, const int trials)
Executes MAX_TRIALS number of XCSF learning iterations using the training data and test iterations us...
double xcs_supervised_score(struct XCSF *xcsf, const struct Input *data, const double *cover)
Calculates the XCSF error for the input data.
double xcs_supervised_score_n(struct XCSF *xcsf, const struct Input *data, const int N, const double *cover)
Calculates the XCSF error for a subsample of the input data.
void xcs_supervised_predict(struct XCSF *xcsf, const double *x, double *pred, const int n_samples, const double *cover)
Calculates the XCSF predictions for the provided input.
Supervised regression learning functions.
void xcsf_store_pset(struct XCSF *xcsf)
Stores the current population.
size_t xcsf_save(const struct XCSF *xcsf, const char *filename)
Writes the current state of XCSF to a file.
void xcsf_retrieve_pset(struct XCSF *xcsf)
Retrieves the previously stored population.
void xcsf_pred_expand(const struct XCSF *xcsf)
Inserts a new hidden layer before the output layer within all prediction neural networks in the popul...
void xcsf_ae_to_classifier(struct XCSF *xcsf, const int y_dim, const int n_del)
Switches from autoencoding to classification.
size_t xcsf_load(struct XCSF *xcsf, const char *filename)
Reads the state of XCSF from a file.
void xcsf_print_pset(const struct XCSF *xcsf, const bool print_cond, const bool print_act, const bool print_pred)
Prints the current XCSF population.
void xcsf_init(struct XCSF *xcsf)
Initialises XCSF with an empty population.
void xcsf_free(struct XCSF *xcsf)
Frees XCSF population sets.