XCSF  1.4.7
XCSF learning classifier system
pybind_wrapper.cpp
Go to the documentation of this file.
1 /*
2  * This program is free software: you can redistribute it and/or modify
3  * it under the terms of the GNU General Public License as published by
4  * the Free Software Foundation, either version 3 of the License, or
5  * (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program. If not, see <http://www.gnu.org/licenses/>.
14  */
15 
25 #ifdef _WIN32 // Try to work around https://bugs.python.org/issue11566
26  #define _hypot hypot
27 #endif
28 
29 #include <chrono>
30 #include <cstdio>
31 #include <fstream>
32 #include <iomanip>
33 #include <iostream>
34 #include <pybind11/numpy.h>
35 #include <pybind11/pybind11.h>
36 #include <pybind11/stl.h>
37 #include <sstream>
38 #include <string>
39 #include <vector>
40 
41 namespace py = pybind11;
42 
43 extern "C" {
44 #include "action.h"
45 #include "clset.h"
46 #include "clset_neural.h"
47 #include "condition.h"
48 #include "ea.h"
49 #include "param.h"
50 #include "prediction.h"
51 #include "utils.h"
52 #include "xcs_rl.h"
53 #include "xcs_supervised.h"
54 }
55 
56 #include "pybind_callback.h"
59 #include "pybind_utils.h"
60 
64 class XCS
65 {
66  private:
67  struct XCSF xcs;
68  double *state;
69  int action;
70  double payoff;
71  struct Input *train_data;
72  struct Input *test_data;
73  struct Input *val_data;
74  py::dict params;
75  py::list metric_train;
76  py::list metric_val;
77  py::list metric_trial;
78  py::list metric_psize;
79  py::list metric_msize;
80  py::list metric_mfrac;
82 
83  public:
87  XCS()
88  {
89  reset();
90  xcsf_init(&xcs);
91  }
92 
97  explicit XCS(py::kwargs kwargs)
98  {
99  reset();
100  set_params(kwargs);
101  xcsf_init(&xcs);
102  }
103 
107  void
108  reset(void)
109  {
110  state = NULL;
111  action = 0;
112  payoff = 0;
113  train_data = new struct Input;
114  train_data->n_samples = 0;
115  train_data->x_dim = 0;
116  train_data->y_dim = 0;
117  train_data->x = NULL;
118  train_data->y = NULL;
119  test_data = new struct Input;
120  test_data->n_samples = 0;
121  test_data->x_dim = 0;
122  test_data->y_dim = 0;
123  test_data->x = NULL;
124  test_data->y = NULL;
125  val_data = NULL;
126  metric_counter = 0;
127  param_init(&xcs, 1, 1, 1);
128  update_params();
129  }
130 
136  size_t
137  save(const char *filename)
138  {
139  return xcsf_save(&xcs, filename);
140  }
141 
147  size_t
148  load(const char *filename)
149  {
150  size_t s = xcsf_load(&xcs, filename);
151  update_params();
152  return s;
153  }
154 
158  void
159  store(void)
160  {
162  }
163 
167  void
168  retrieve(void)
169  {
171  }
172 
176  void
178  {
179  param_print(&xcs);
180  }
181 
186  void
188  {
190  }
191 
197  void
198  ae_to_classifier(const int y_dim, const int n_del)
199  {
200  xcsf_ae_to_classifier(&xcs, y_dim, n_del);
201  }
202 
209  void
210  print_pset(const bool condition, const bool action, const bool prediction)
211  {
212  xcsf_print_pset(&xcs, condition, action, prediction);
213  }
214 
215  /* Reinforcement learning */
216 
224  double
225  fit(const py::array_t<double> input, const int action, const double reward)
226  {
227  py::buffer_info buf = input.request();
228  if (buf.shape[0] != xcs.x_dim) {
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());
232  }
233  if (action < 0 || action >= xcs.n_actions) {
234  std::ostringstream error;
235  error << "fit(): action outside: [0," << xcs.n_actions << ")"
236  << std::endl;
237  throw std::invalid_argument(error.str());
238  }
239  state = (double *) buf.ptr;
240  return xcs_rl_fit(&xcs, state, action, reward);
241  }
242 
246  void
248  {
250  }
251 
255  void
256  end_trial(void)
257  {
259  }
260 
264  void
265  init_step(void)
266  {
268  }
269 
273  void
274  end_step(void)
275  {
277  }
278 
286  int
287  decision(const py::array_t<double> input, const bool explore)
288  {
289  py::buffer_info buf = input.request();
290  if (buf.shape[0] != xcs.x_dim) {
291  std::ostringstream error;
292  error << "decision(): x_dim is not equal to: " << xcs.x_dim;
293  throw std::invalid_argument(error.str());
294  }
295  state = (double *) buf.ptr;
296  param_set_explore(&xcs, explore);
298  return action;
299  }
300 
307  void
308  update(const double reward, const bool done)
309  {
310  payoff = reward;
311  xcs_rl_update(&xcs, state, action, payoff, done);
312  }
313 
321  double
322  error(const double reward, const bool done, const double max_p)
323  {
324  payoff = reward;
325  return xcs_rl_error(&xcs, action, payoff, done, max_p);
326  }
327 
328  /* Supervised learning */
329 
336  void
337  load_input(struct Input *data, const py::array_t<double> X,
338  const py::array_t<double> Y)
339  {
340  // access input
341  const py::buffer_info buf_x = X.request();
342  const py::buffer_info buf_y = Y.request();
343  // check input shape
344  if (buf_x.ndim < 1 || buf_x.ndim > 2) {
345  throw std::invalid_argument("X must be 1 or 2-D array");
346  }
347  if (buf_y.ndim < 1 || buf_y.ndim > 2) {
348  throw std::invalid_argument("Y must be 1 or 2-D array");
349  }
350  if (buf_x.shape[0] != buf_y.shape[0]) {
351  throw std::invalid_argument("X and Y n_samples are not equal");
352  }
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] << ")";
357  error << " but expected (" << xcs.x_dim << ")" << std::endl;
358  error << "Perhaps reshape your data.";
359  throw std::invalid_argument(error.str());
360  }
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] << ")";
365  error << " but expected (" << xcs.y_dim << ")" << std::endl;
366  error << "Perhaps reshape your data.";
367  throw std::invalid_argument(error.str());
368  }
369  // load input
370  data->n_samples = buf_x.shape[0];
371  data->x_dim = xcs.x_dim;
372  data->y_dim = xcs.y_dim;
373  data->x = static_cast<double *>(buf_x.ptr);
374  data->y = static_cast<double *>(buf_y.ptr);
375  }
376 
380  void
382  {
383  double trial = py::cast<double>(metric_trial[metric_trial.size() - 1]);
384  double train = py::cast<double>(metric_train[metric_train.size() - 1]);
385  double psize = py::cast<double>(metric_psize[metric_psize.size() - 1]);
386  double msize = py::cast<double>(metric_msize[metric_msize.size() - 1]);
387  double mfrac = py::cast<double>(metric_mfrac[metric_mfrac.size() - 1]);
388  std::ostringstream status;
389  status << get_timestamp();
390  status << " trials=" << trial;
391  status << " train=" << std::fixed << std::setprecision(5) << train;
392  if (val_data != NULL) {
393  double val = py::cast<double>(metric_val[metric_val.size() - 1]);
394  status << " val=" << std::fixed << std::setprecision(5) << val;
395  }
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());
400  }
401 
408  void
409  update_metrics(const double train, const double val, const int n_trials)
410  {
411  const int trial = (1 + metric_counter) * n_trials;
412  metric_train.append(train);
413  metric_val.append(val);
414  metric_trial.append(trial);
415  metric_psize.append(xcs.pset.size);
416  metric_msize.append(xcs.mset_size);
417  metric_mfrac.append(xcs.mfrac);
418  ++metric_counter;
419  }
420 
425  void
426  load_validation_data(py::kwargs kwargs)
427  {
428  val_data = NULL;
429  if (kwargs.contains("validation_data")) {
430  py::tuple data = kwargs["validation_data"].cast<py::tuple>();
431  if (data) {
432  if (data.size() != 2) {
433  throw std::invalid_argument(
434  "validation_data must be a tuple with two arrays");
435  }
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>>();
438  load_input(test_data, X_val, y_val);
440  // use zeros for validation predictions instead of covering
441  memset(xcs.cover, 0, sizeof(double) * xcs.pa_size);
442  }
443  }
444  }
445 
451  bool
452  callbacks_run(py::list callbacks)
453  {
454  bool terminate = false;
455  py::dict metrics = get_metrics();
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)) {
460  terminate = true;
461  }
462  } else {
463  throw std::invalid_argument("unsupported callback");
464  }
465  }
466  return terminate;
467  }
468 
473  void
474  callbacks_finish(py::list callbacks)
475  {
476  for (py::handle item : callbacks) {
477  if (py::isinstance<Callback>(item)) {
478  Callback *cb = py::cast<Callback *>(item);
479  cb->finish(&xcs);
480  } else {
481  throw std::invalid_argument("unsupported callback");
482  }
483  }
484  }
485 
498  XCS &
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)
502  {
503  if (!warm_start) { // re-initialise XCSF as necessary
504  xcsf_free(&xcs);
505  xcsf_init(&xcs);
506  }
507  load_input(train_data, X_train, y_train);
508  load_validation_data(kwargs);
509  // get callbacks
510  py::list calls;
511  if (py::isinstance<py::list>(callbacks)) {
512  calls = callbacks.cast<py::list>();
513  }
514  // break up the learning into epochs to track metrics
515  const int n = ceil(xcs.MAX_TRIALS / (double) xcs.PERF_TRIALS);
516  const int n_trials = std::min(xcs.MAX_TRIALS, xcs.PERF_TRIALS);
517  for (int i = 0; i < n; ++i) {
518  const int start = i * n_trials;
519  const double train_error = xcs_supervised_fit(
520  &xcs, train_data, NULL, shuffle, start, n_trials);
521  double val_error = 0;
522  if (val_data != NULL) {
523  val_error = xcs_supervised_score(&xcs, val_data, xcs.cover);
524  }
525  update_metrics(train_error, val_error, n_trials);
526  if (verbose) {
527  print_status();
528  }
529  if (callbacks_run(calls)) {
530  break;
531  }
532  }
533  callbacks_finish(calls);
534  return *this;
535  }
536 
542  double *
543  get_cover(const py::array_t<double> cover)
544  {
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 << ")"
549  << std::endl;
550  throw std::invalid_argument(err.str());
551  }
552  if (buf_c.shape[0] != xcs.y_dim) {
553  std::ostringstream err;
554  err << "cover length = " << buf_c.shape[0] << " but expected "
555  << xcs.y_dim << std::endl;
556  throw std::invalid_argument(err.str());
557  }
558  return reinterpret_cast<double *>(buf_c.ptr);
559  }
560 
565  void
566  set_cover(const py::object &cover)
567  {
568  if (cover.is_none()) {
569  memset(xcs.cover, 0, sizeof(double) * xcs.pa_size);
570  } else {
571  py::array_t<double> cover_arr = cover.cast<py::array_t<double>>();
572  xcs.cover = get_cover(cover_arr);
573  }
574  }
575 
583  py::array_t<double>
584  predict(const py::array_t<double> X, const py::object &cover)
585  {
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");
589  }
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] << ")";
594  error << " but expected (" << xcs.x_dim << ")" << std::endl;
595  error << "Perhaps reshape your data.";
596  throw std::invalid_argument(error.str());
597  }
598  const int n_samples = buf_x.shape[0];
599  const double *input = reinterpret_cast<double *>(buf_x.ptr);
600  double *output =
601  (double *) malloc(sizeof(double) * n_samples * xcs.pa_size);
602  set_cover(cover);
603  xcs_supervised_predict(&xcs, input, output, n_samples, xcs.cover);
604  return py::array_t<double>(
605  std::vector<ptrdiff_t>{ n_samples, xcs.pa_size }, output);
606  }
607 
617  double
618  score(const py::array_t<double> X, const py::array_t<double> Y, const int N,
619  const py::object &cover)
620  {
621  set_cover(cover);
622  load_input(test_data, X, Y);
623  if (N > 1) {
625  }
627  }
628 
634  py::bytes
635  serialize() const
636  {
637  // Write XCSF to a temporary binary file
638  const char *filename = "_tmp_pickle.bin";
639  xcsf_save(&xcs, filename);
640  // Read the binary file into bytes
641  std::ifstream file(filename, std::ios::binary);
642  std::string state((std::istreambuf_iterator<char>(file)),
643  std::istreambuf_iterator<char>());
644  file.close();
645  // Delete the temporary file
646  if (std::remove(filename) != 0) {
647  perror("Error deleting temporary pickle file");
648  }
649  // Return the binary data as bytes
650  return py::bytes(state);
651  }
652 
658  static XCS
659  deserialize(const py::bytes &state)
660  {
661  // Write the XCSF bytes to a temporary binary file
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());
666  file.close();
667  // Create a new XCSF instance
668  XCS xcs = XCS();
669  // Load XCSF
670  xcsf_load(&xcs.xcs, filename);
671  // Update object params
672  xcs.update_params();
673  // Delete the temporary file
674  if (std::remove(filename) != 0) {
675  perror("Error deleting temporary pickle file");
676  }
677  // Return the deserialized XCSF
678  return xcs;
679  }
680 
681  /* GETTERS */
682 
687  double
688  error(void)
689  {
690  return xcs.error;
691  }
692 
693  py::dict
695  {
696  py::dict metrics;
697  metrics["train"] = metric_train;
698  metrics["val"] = metric_val;
699  metrics["trials"] = metric_trial;
700  metrics["psize"] = metric_psize;
701  metrics["msize"] = metric_msize;
702  metrics["mfrac"] = metric_mfrac;
703  return metrics;
704  }
705 
706  int
708  {
709  return xcs.pset.size;
710  }
711 
712  int
714  {
715  return xcs.pset.num;
716  }
717 
718  int
719  get_time(void)
720  {
721  return xcs.time;
722  }
723 
724  double
726  {
727  return clset_mean_cond_size(&xcs, &xcs.pset);
728  }
729 
730  double
732  {
733  return clset_mean_pred_size(&xcs, &xcs.pset);
734  }
735 
736  double
737  get_pset_mean_pred_eta(const int layer)
738  {
739  return clset_mean_pred_eta(&xcs, &xcs.pset, layer);
740  }
741 
742  double
743  get_pset_mean_pred_neurons(const int layer)
744  {
745  return clset_mean_pred_neurons(&xcs, &xcs.pset, layer);
746  }
747 
748  double
750  {
751  return clset_mean_pred_connections(&xcs, &xcs.pset, layer);
752  }
753 
754  double
756  {
757  return clset_mean_pred_layers(&xcs, &xcs.pset);
758  }
759 
760  double
762  {
763  return clset_mean_cond_connections(&xcs, &xcs.pset, layer);
764  }
765 
766  double
767  get_pset_mean_cond_neurons(const int layer)
768  {
769  return clset_mean_cond_neurons(&xcs, &xcs.pset, layer);
770  }
771 
772  double
774  {
775  return clset_mean_cond_layers(&xcs, &xcs.pset);
776  }
777 
778  double
780  {
781  return xcs.mset_size;
782  }
783 
784  double
786  {
787  return xcs.aset_size;
788  }
789 
790  double
791  get_mfrac(void)
792  {
793  return xcs.mfrac;
794  }
795 
796  /* JSON */
797 
805  const char *
806  json_export(const bool condition, const bool action, const bool prediction)
807  {
808  if (xcs.pset.list != NULL) {
809  return clset_json_export(&xcs, &xcs.pset, condition, action,
810  prediction);
811  }
812  return "null";
813  }
814 
818  void
820  {
821  char *json_str = param_json_export(&xcs);
822  py::module json = py::module::import("json");
823  py::object parsed_json = json.attr("loads")(json_str);
824  py::dict result(parsed_json);
825  params = result;
826  // map None types
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();
831  }
832  }
833  free(json_str);
834  }
835 
841  py::dict
842  get_params(const bool deep)
843  {
844  (void) deep;
845  return params;
846  }
847 
853  XCS &
854  set_params(py::kwargs kwargs)
855  {
856  py::dict kwargs_dict(kwargs);
857  // update external params dict
858  for (const auto &item : kwargs_dict) {
859  params[item.first] = item.second;
860  }
861  // map None types
862  if (kwargs_dict.contains("random_state")) {
863  py::object rs = kwargs["random_state"];
864  if (rs.is_none()) {
865  kwargs_dict["random_state"] = -1;
866  }
867  }
868  // convert dict to JSON and parse parameters
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();
873  param_json_import(&xcs, json_params);
874  return *this;
875  }
876 
881  py::dict
883  {
884  char *json_str = param_json_export(&xcs);
885  py::module json_module = py::module::import("json");
886  py::dict internal_params = json_module.attr("loads")(json_str);
887  free(json_str);
888  return internal_params;
889  }
890 
895  void
896  json_insert_cl(const std::string &json_str)
897  {
898  cJSON *json = cJSON_Parse(json_str.c_str());
900  clset_json_insert_cl(&xcs, json);
901  cJSON_Delete(json);
902  }
903 
908  void
909  json_insert(const std::string &json_str)
910  {
911  clset_json_insert(&xcs, json_str.c_str());
912  }
913 
918  void
919  json_write(const std::string &filename)
920  {
921  std::ofstream outfile(filename);
922  outfile << json_export(true, true, true);
923  outfile.close();
924  }
925 
930  void
931  json_read(const std::string &filename)
932  {
933  std::ifstream infile(filename);
934  std::stringstream buffer;
935  buffer << infile.rdbuf();
936  json_insert(buffer.str());
937  }
938 };
939 
941 {
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";
945 
946  double (XCS::*fit1)(const py::array_t<double>, const int, const double) =
947  &XCS::fit;
948  XCS &(XCS::*fit2)(const py::array_t<double>, const py::array_t<double>,
949  const bool, const bool, const bool, py::object,
950  py::kwargs) = &XCS::fit;
951 
952  double (XCS::*error1)(void) = &XCS::error;
953  double (XCS::*error2)(const double, const bool, const double) = &XCS::error;
954 
955  py::class_<Callback, std::unique_ptr<Callback, py::nodelete>>(m,
956  "Callback");
957 
958  py::class_<EarlyStoppingCallback, Callback,
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);
966 
967  py::class_<CheckpointCallback, Callback,
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);
975 
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.")
980  .def("fit", fit1,
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"))
984  .def("fit", fit2,
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())
991  .def(
992  "score", &XCS::score,
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 "
997  "otherwise zeros.",
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 "
1002  "size BETA.")
1003  .def("error", error2,
1004  "Returns the reinforcement learning system prediction error.",
1005  py::arg("reward"), py::arg("done"), py::arg("max_p"))
1006  .def("predict", &XCS::predict,
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())
1013  .def("save", &XCS::save,
1014  "Saves the current state of XCSF to persistent storage.",
1015  py::arg("filename"))
1016  .def("load", &XCS::load,
1017  "Loads the current state of XCSF from persistent storage.",
1018  py::arg("filename"))
1019  .def("store", &XCS::store,
1020  "Stores the current XCSF population in memory for later "
1021  "retrieval, overwriting any previously stored population.")
1022  .def("retrieve", &XCS::retrieve,
1023  "Retrieves the previously stored XCSF population from memory.")
1024  .def("init_trial", &XCS::init_trial, "Initialises a multi-step trial.")
1025  .def("end_trial", &XCS::end_trial, "Ends a multi-step trial.")
1026  .def("init_step", &XCS::init_step,
1027  "Initialises a step in a multi-step trial.")
1028  .def("end_step", &XCS::end_step, "Ends a step in a multi-step trial.")
1029  .def("decision", &XCS::decision,
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"))
1033  .def("update", &XCS::update,
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.")
1037  .def("get_metrics", &XCS::get_metrics,
1038  "Returns a dictionary of performance metrics.")
1039  .def("pset_size", &XCS::get_pset_size,
1040  "Returns the number of macro-classifiers in the population.")
1041  .def("pset_num", &XCS::get_pset_num,
1042  "Returns the number of micro-classifiers in the population.")
1043  .def("pset_mean_cond_size", &XCS::get_pset_mean_cond_size,
1044  "Returns the average condition size of classifiers in the "
1045  "population.")
1046  .def("pset_mean_pred_size", &XCS::get_pset_mean_pred_size,
1047  "Returns the average prediction size of classifiers in the "
1048  "population.")
1049  .def("pset_mean_pred_eta", &XCS::get_pset_mean_pred_eta,
1050  "Returns the mean eta for a prediction layer.", py::arg("layer"))
1051  .def("pset_mean_pred_neurons", &XCS::get_pset_mean_pred_neurons,
1052  "Returns the mean number of neurons for a prediction layer.",
1053  py::arg("layer"))
1054  .def("pset_mean_pred_layers", &XCS::get_pset_mean_pred_layers,
1055  "Returns the mean number of layers in the prediction networks.")
1056  .def("pset_mean_pred_connections", &XCS::get_pset_mean_pred_connections,
1057  "Returns the mean number of connections for a prediction layer.",
1058  py::arg("layer"))
1059  .def("pset_mean_cond_neurons", &XCS::get_pset_mean_cond_neurons,
1060  "Returns the mean number of neurons for a condition layer.",
1061  py::arg("layer"))
1062  .def("pset_mean_cond_layers", &XCS::get_pset_mean_cond_layers,
1063  "Returns the mean number of layers in the condition networks.")
1064  .def("pset_mean_cond_connections", &XCS::get_pset_mean_cond_connections,
1065  "Returns the mean number of connections for a condition layer.",
1066  py::arg("layer"))
1067  .def("mset_size", &XCS::get_mset_size,
1068  "Returns the average match set size.")
1069  .def("aset_size", &XCS::get_aset_size,
1070  "Returns the average action set size.")
1071  .def("mfrac", &XCS::get_mfrac,
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)
1076  .def("print_params", &XCS::print_params,
1077  "Prints the XCSF parameters and their current values.")
1078  .def("pred_expand", &XCS::pred_expand,
1079  "Inserts a new hidden layer before the output layer within all "
1080  "prediction neural networks in the population.")
1081  .def("ae_to_classifier", &XCS::ae_to_classifier,
1082  "Switches from autoencoding to classification.", py::arg("y_dim"),
1083  py::arg("n_del"))
1084  .def("json", &XCS::json_export,
1085  "Returns a JSON formatted string representing the population set.",
1086  py::arg("condition") = true, py::arg("action") = true,
1087  py::arg("prediction") = true)
1088  .def("json_write", &XCS::json_write,
1089  "Writes the current population set to a file in JSON.",
1090  py::arg("filename"))
1091  .def("json_read", &XCS::json_read,
1092  "Reads classifiers from a JSON file and adds to the population.",
1093  py::arg("filename"))
1094  .def("get_params", &XCS::get_params, py::arg("deep") = true,
1095  "Returns a dictionary of parameters and their values.")
1096  .def("set_params", &XCS::set_params, "Sets parameters.")
1097  .def("json_insert_cl", &XCS::json_insert_cl,
1098  "Creates a classifier from JSON and inserts into the population.",
1099  py::arg("json_str"))
1100  .def("json_insert", &XCS::json_insert,
1101  "Creates classifiers from JSON and inserts into the population.",
1102  py::arg("json_str"))
1103  .def("internal_params", &XCS::internal_params, "Gets internal params.")
1104  .def(py::pickle(
1105  [](const XCS &obj) { return obj.serialize(); },
1106  [](const py::bytes &state) { return XCS::deserialize(state); }));
1107 }
Interface for classifier actions.
Interface for Callbacks.
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.
int get_time(void)
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.
py::list metric_msize
double get_aset_size(void)
py::list metric_val
struct Input * test_data
Test data for supervised learning.
py::list metric_train
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::list metric_trial
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...
double get_mfrac(void)
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.
py::list metric_mfrac
int get_pset_size(void)
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.
py::list metric_psize
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.
int metric_counter
const char * json_export(const bool condition, const bool action, const bool prediction)
Returns a JSON formatted string representing the population set.
int get_pset_num(void)
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.
Definition: clset.c:693
double clset_mean_cond_size(const struct XCSF *xcsf, const struct Set *set)
Calculates the mean condition size of classifiers in the set.
Definition: clset.c:673
void clset_json_insert(struct XCSF *xcsf, const char *json_str)
Creates classifiers from JSON and inserts into the population.
Definition: clset.c:796
void clset_json_insert_cl(struct XCSF *xcsf, const cJSON *json)
Creates a classifier from cJSON and inserts in the population set.
Definition: clset.c:780
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.
Definition: clset.c:754
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.
Definition: clset_neural.c:65
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.
Definition: clset_neural.c:173
double clset_mean_cond_layers(const struct XCSF *xcsf, const struct Set *set)
Calculates the mean number of condition layers in the set.
Definition: clset_neural.c:39
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.
Definition: clset_neural.c:200
double clset_mean_pred_layers(const struct XCSF *xcsf, const struct Set *set)
Calculates the mean number of prediction layers in the set.
Definition: clset_neural.c:147
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.
Definition: clset_neural.c:93
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.
Definition: clset_neural.c:121
Functions operating on sets of neural classifiers.
Interface for classifier conditions.
Evolutionary algorithm functions.
Definition: __init__.py:1
void param_json_import(struct XCSF *xcsf, const char *json_str)
Sets the parameters from a json formatted string.
Definition: param.c:437
void param_print(const struct XCSF *xcsf)
Prints all XCSF parameters.
Definition: param.c:481
const char * param_set_explore(struct XCSF *xcsf, const bool a)
Definition: param.c:881
char * param_json_export(const struct XCSF *xcsf)
Returns a json formatted string representation of the parameters.
Definition: param.c:113
void param_init(struct XCSF *xcsf, const int x_dim, const int y_dim, const int n_actions)
Initialises default XCSF parameters.
Definition: param.c:45
Functions for setting and printing parameters.
Interface for classifier predictions.
Interface for callbacks.
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.
Definition: pybind_utils.h:31
PYBIND11_MODULE(xcsf, m)
Input data structure.
Definition: xcsf.h:146
double * x
Feature variables.
Definition: xcsf.h:147
int y_dim
Number of target variables.
Definition: xcsf.h:150
int x_dim
Number of feature variables.
Definition: xcsf.h:149
int n_samples
Number of instances.
Definition: xcsf.h:151
double * y
Target variables.
Definition: xcsf.h:148
int size
Number of macro-classifiers.
Definition: xcsf.h:78
struct Clist * list
Linked list of classifiers.
Definition: xcsf.h:77
int num
The total numerosity of classifiers.
Definition: xcsf.h:79
XCSF data structure.
Definition: xcsf.h:85
double mset_size
Average match set size.
Definition: xcsf.h:99
int x_dim
Number of problem input variables.
Definition: xcsf.h:110
int PERF_TRIALS
Number of problem instances to avg performance output.
Definition: xcsf.h:128
double aset_size
Average action set size.
Definition: xcsf.h:100
int pa_size
Prediction array size.
Definition: xcsf.h:109
int n_actions
Number of class labels / actions.
Definition: xcsf.h:112
struct Set pset
Population set.
Definition: xcsf.h:86
int MAX_TRIALS
Number of problem instances to run in one experiment.
Definition: xcsf.h:127
double * cover
Values to return for a prediction instead of covering.
Definition: xcsf.h:107
double error
Average system error.
Definition: xcsf.h:98
int time
Current number of EA executions.
Definition: xcsf.h:108
double mfrac
Generalisation measure.
Definition: xcsf.h:101
int y_dim
Number of problem output variables.
Definition: xcsf.h:111
void utils_json_parse_check(const cJSON *json)
Checks whether JSON parsed correctly.
Definition: utils.c:109
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).
Definition: xcs_rl.c:104
void xcs_rl_update(struct XCSF *xcsf, const double *state, const int action, const double reward, const bool done)
Provides reinforcement to the sets.
Definition: xcs_rl.c:192
int xcs_rl_decision(struct XCSF *xcsf, const double *state)
Selects an action to perform in a reinforcement learning problem.
Definition: xcs_rl.c:247
void xcs_rl_init_step(struct XCSF *xcsf)
Initialises a step in a reinforcement learning trial.
Definition: xcs_rl.c:157
void xcs_rl_end_trial(struct XCSF *xcsf)
Frees memory used by a reinforcement learning trial.
Definition: xcs_rl.c:145
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.
Definition: xcs_rl.c:223
void xcs_rl_init_trial(struct XCSF *xcsf)
Initialises a reinforcement learning trial.
Definition: xcs_rl.c:126
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.
Definition: xcs_rl.c:171
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.
Definition: xcsf.c:195
size_t xcsf_save(const struct XCSF *xcsf, const char *filename)
Writes the current state of XCSF to a file.
Definition: xcsf.c:90
void xcsf_retrieve_pset(struct XCSF *xcsf)
Retrieves the previously stored population.
Definition: xcsf.c:213
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...
Definition: xcsf.c:151
void xcsf_ae_to_classifier(struct XCSF *xcsf, const int y_dim, const int n_del)
Switches from autoencoding to classification.
Definition: xcsf.c:171
size_t xcsf_load(struct XCSF *xcsf, const char *filename)
Reads the state of XCSF from a file.
Definition: xcsf.c:114
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.
Definition: xcsf.c:77
void xcsf_init(struct XCSF *xcsf)
Initialises XCSF with an empty population.
Definition: xcsf.c:37
void xcsf_free(struct XCSF *xcsf)
Frees XCSF population sets.
Definition: xcsf.c:56