XCSF 1.5.1
XCSF learning classifier system
Loading...
Searching...
No Matches
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 <cstdio>
30#include <fstream>
31#include <iomanip>
32#include <iostream>
33#include <pybind11/numpy.h>
34#include <pybind11/pybind11.h>
35#include <pybind11/stl.h>
36#include <sstream>
37#include <string>
38
39namespace py = pybind11;
40
41extern "C" {
42#include "clset.h"
43#include "clset_neural.h"
44#include "param.h"
45#include "utils.h"
46#include "xcs_rl.h"
47#include "xcs_supervised.h"
48}
49
50#include "pybind_callback.h"
53#include "pybind_utils.h"
54
58class XCS
59{
60 private:
61 struct XCSF xcs;
62 double *state;
63 int action;
64 double payoff;
65 struct Input *train_data;
66 struct Input *test_data;
67 struct Input *val_data;
68 py::dict params;
69 py::list metric_train;
70 py::list metric_val;
71 py::list metric_trial;
72 py::list metric_psize;
73 py::list metric_msize;
74 py::list metric_mfrac;
76
77 public:
82 {
83 reset();
84 xcsf_init(&xcs);
85 }
86
91 explicit XCS(py::kwargs kwargs)
92 {
93 reset();
94 set_params(kwargs);
95 xcsf_init(&xcs);
96 }
97
101 void
102 reset(void)
103 {
104 state = NULL;
105 action = 0;
106 payoff = 0;
107 train_data = new struct Input;
109 train_data->x_dim = 0;
110 train_data->y_dim = 0;
111 train_data->x = NULL;
112 train_data->y = NULL;
113 test_data = new struct Input;
114 test_data->n_samples = 0;
115 test_data->x_dim = 0;
116 test_data->y_dim = 0;
117 test_data->x = NULL;
118 test_data->y = NULL;
119 val_data = NULL;
120 metric_counter = 0;
121 param_init(&xcs, 1, 1, 1);
123 }
124
130 size_t
131 save(const char *filename)
132 {
133 return xcsf_save(&xcs, filename);
134 }
135
141 size_t
142 load(const char *filename)
143 {
144 size_t s = xcsf_load(&xcs, filename);
146 return s;
147 }
148
152 void
153 store(void)
154 {
156 }
157
161 void
163 {
165 }
166
170 void
172 {
174 }
175
180 void
182 {
184 }
185
191 void
192 ae_to_classifier(const int y_dim, const int n_del)
193 {
194 xcsf_ae_to_classifier(&xcs, y_dim, n_del);
195 }
196
203 void
204 print_pset(const bool condition, const bool action, const bool prediction)
205 {
206 xcsf_print_pset(&xcs, condition, action, prediction);
207 }
208
209 /* Reinforcement learning */
210
218 double
219 fit(const py::array_t<double> input, const int action, const double reward)
220 {
221 py::buffer_info buf = input.request();
222 if (buf.shape[0] != xcs.x_dim) {
223 std::ostringstream error;
224 error << "fit(): x_dim is not equal to: " << xcs.x_dim << std::endl;
225 throw std::invalid_argument(error.str());
226 }
227 if (action < 0 || action >= xcs.n_actions) {
228 std::ostringstream error;
229 error << "fit(): action outside: [0," << xcs.n_actions << ")"
230 << std::endl;
231 throw std::invalid_argument(error.str());
232 }
233 state = (double *) buf.ptr;
234 return xcs_rl_fit(&xcs, state, action, reward);
235 }
236
240 void
242 {
244 }
245
249 void
251 {
253 }
254
258 void
260 {
262 }
263
267 void
269 {
271 }
272
280 int
281 decision(const py::array_t<double> input, const bool explore)
282 {
283 py::buffer_info buf = input.request();
284 if (buf.shape[0] != xcs.x_dim) {
285 std::ostringstream error;
286 error << "decision(): x_dim is not equal to: " << xcs.x_dim;
287 throw std::invalid_argument(error.str());
288 }
289 state = (double *) buf.ptr;
290 param_set_explore(&xcs, explore);
292 return action;
293 }
294
301 void
302 update(const double reward, const bool done)
303 {
304 payoff = reward;
306 }
307
315 double
316 error(const double reward, const bool done, const double max_p)
317 {
318 payoff = reward;
319 return xcs_rl_error(&xcs, action, payoff, done, max_p);
320 }
321
322 /* Supervised learning */
323
330 void
331 load_input(struct Input *data, const py::array_t<double> X,
332 const py::array_t<double> Y)
333 {
334 // access input
335 const py::buffer_info buf_x = X.request();
336 const py::buffer_info buf_y = Y.request();
337 // check array contiguity
338 // https://github.com/pybind/pybind11/discussions/4211#discussioncomment-3905115
339 const int C_CONTIGUOUS =
340 py::detail::npy_api::constants::NPY_ARRAY_C_CONTIGUOUS_;
341 if (!(C_CONTIGUOUS == (X.flags() & C_CONTIGUOUS)) ||
342 !(C_CONTIGUOUS == (Y.flags() & C_CONTIGUOUS))) {
343 throw std::invalid_argument("X and Y must be C-contiguous");
344 }
345 // check input shape
346 if (buf_x.ndim < 1 || buf_x.ndim > 2) {
347 throw std::invalid_argument("X must be 1 or 2-D array");
348 }
349 if (buf_y.ndim < 1 || buf_y.ndim > 2) {
350 throw std::invalid_argument("Y must be 1 or 2-D array");
351 }
352 if (buf_x.shape[0] != buf_y.shape[0]) {
353 throw std::invalid_argument("X and Y n_samples are not equal");
354 }
355 if (buf_x.ndim > 1 && buf_x.shape[1] != xcs.x_dim) {
356 std::ostringstream error;
357 error << "load_input():";
358 error << " received x_dim: (" << buf_x.shape[1] << ")";
359 error << " but expected (" << xcs.x_dim << ")" << std::endl;
360 error << "Perhaps reshape your data.";
361 throw std::invalid_argument(error.str());
362 }
363 if (buf_y.ndim > 1 && buf_y.shape[1] != xcs.y_dim) {
364 std::ostringstream error;
365 error << "load_input():";
366 error << " received y_dim: (" << buf_y.shape[1] << ")";
367 error << " but expected (" << xcs.y_dim << ")" << std::endl;
368 error << "Perhaps reshape your data.";
369 throw std::invalid_argument(error.str());
370 }
371 // load input
372 data->n_samples = buf_x.shape[0];
373 data->x_dim = xcs.x_dim;
374 data->y_dim = xcs.y_dim;
375 data->x = static_cast<double *>(buf_x.ptr);
376 data->y = static_cast<double *>(buf_y.ptr);
377 }
378
382 void
384 {
385 double trial = py::cast<double>(metric_trial[metric_trial.size() - 1]);
386 double train = py::cast<double>(metric_train[metric_train.size() - 1]);
387 double psize = py::cast<double>(metric_psize[metric_psize.size() - 1]);
388 double msize = py::cast<double>(metric_msize[metric_msize.size() - 1]);
389 double mfrac = py::cast<double>(metric_mfrac[metric_mfrac.size() - 1]);
390 std::ostringstream status;
391 status << get_timestamp();
392 status << " trials=" << trial;
393 status << " train=" << std::fixed << std::setprecision(5) << train;
394 if (val_data != NULL) {
395 double val = py::cast<double>(metric_val[metric_val.size() - 1]);
396 status << " val=" << std::fixed << std::setprecision(5) << val;
397 }
398 status << " pset=" << std::fixed << std::setprecision(1) << psize;
399 status << " mset=" << std::fixed << std::setprecision(1) << msize;
400 status << " mfrac=" << std::fixed << std::setprecision(2) << mfrac;
401 py::print(status.str());
402 }
403
410 void
411 update_metrics(const double train, const double val, const int n_trials)
412 {
413 const int trial = (1 + metric_counter) * n_trials;
414 metric_train.append(train);
415 metric_val.append(val);
416 metric_trial.append(trial);
417 metric_psize.append(xcs.pset.size);
418 metric_msize.append(xcs.mset_size);
419 metric_mfrac.append(xcs.mfrac);
421 }
422
427 void
428 load_validation_data(py::kwargs kwargs)
429 {
430 val_data = NULL;
431 if (kwargs.contains("validation_data")) {
432 py::tuple data = kwargs["validation_data"].cast<py::tuple>();
433 if (data) {
434 if (data.size() != 2) {
435 throw std::invalid_argument(
436 "validation_data must be a tuple with two arrays");
437 }
438 py::array_t<double> X_val = data[0].cast<py::array_t<double>>();
439 py::array_t<double> y_val = data[1].cast<py::array_t<double>>();
440 load_input(test_data, X_val, y_val);
442 // use zeros for validation predictions instead of covering
443 memset(xcs.cover, 0, sizeof(double) * xcs.pa_size);
444 }
445 }
446 }
447
453 bool
454 callbacks_run(py::list callbacks)
455 {
456 bool terminate = false;
457 py::dict metrics = get_metrics();
458 for (py::handle item : callbacks) {
459 if (py::isinstance<Callback>(item)) {
460 Callback *cb = py::cast<Callback *>(item);
461 if (cb->run(&xcs, metrics)) {
462 terminate = true;
463 }
464 } else {
465 throw std::invalid_argument("unsupported callback");
466 }
467 }
468 return terminate;
469 }
470
475 void
476 callbacks_finish(py::list callbacks)
477 {
478 for (py::handle item : callbacks) {
479 if (py::isinstance<Callback>(item)) {
480 Callback *cb = py::cast<Callback *>(item);
481 cb->finish(&xcs);
482 } else {
483 throw std::invalid_argument("unsupported callback");
484 }
485 }
486 }
487
500 XCS &
501 fit(const py::array_t<double> X_train, const py::array_t<double> y_train,
502 const bool shuffle, const bool warm_start, const bool verbose,
503 py::object callbacks, py::kwargs kwargs)
504 {
505 if (!warm_start) { // re-initialise XCSF as necessary
506 xcsf_free(&xcs);
507 xcsf_init(&xcs);
508 }
509 load_input(train_data, X_train, y_train);
510 load_validation_data(kwargs);
511 // get callbacks
512 py::list calls;
513 if (py::isinstance<py::list>(callbacks)) {
514 calls = callbacks.cast<py::list>();
515 }
516 // break up the learning into epochs to track metrics
517 const int n = ceil(xcs.MAX_TRIALS / (double) xcs.PERF_TRIALS);
518 const int n_trials = std::min(xcs.MAX_TRIALS, xcs.PERF_TRIALS);
519 for (int i = 0; i < n; ++i) {
520 const int start = i * n_trials;
521 const double train_error = xcs_supervised_fit(
522 &xcs, train_data, NULL, shuffle, start, n_trials);
523 double val_error = 0;
524 if (val_data != NULL) {
525 val_error = xcs_supervised_score(&xcs, val_data, xcs.cover);
526 }
527 update_metrics(train_error, val_error, n_trials);
528 if (verbose) {
529 print_status();
530 }
531 if (callbacks_run(calls)) {
532 break;
533 }
534 }
535 callbacks_finish(calls);
536 return *this;
537 }
538
544 double *
545 get_cover(const py::array_t<double> cover)
546 {
547 const py::buffer_info buf_c = cover.request();
548 if (buf_c.ndim != 1) {
549 std::ostringstream err;
550 err << "cover must be an array of shape (1, " << xcs.y_dim << ")"
551 << std::endl;
552 throw std::invalid_argument(err.str());
553 }
554 if (buf_c.shape[0] != xcs.y_dim) {
555 std::ostringstream err;
556 err << "cover length = " << buf_c.shape[0] << " but expected "
557 << xcs.y_dim << std::endl;
558 throw std::invalid_argument(err.str());
559 }
560 return reinterpret_cast<double *>(buf_c.ptr);
561 }
562
567 void
568 set_cover(const py::object &cover)
569 {
570 if (cover.is_none()) {
571 memset(xcs.cover, 0, sizeof(double) * xcs.pa_size);
572 } else {
573 py::array_t<double> cover_arr = cover.cast<py::array_t<double>>();
574 xcs.cover = get_cover(cover_arr);
575 }
576 }
577
585 py::array_t<double>
586 predict(const py::array_t<double> X, const py::object &cover)
587 {
588 const py::buffer_info buf_x = X.request();
589 // check array contiguity
590 // https://github.com/pybind/pybind11/discussions/4211#discussioncomment-3905115
591 const int C_CONTIGUOUS =
592 py::detail::npy_api::constants::NPY_ARRAY_C_CONTIGUOUS_;
593 if (!(C_CONTIGUOUS == (X.flags() & C_CONTIGUOUS))) {
594 throw std::invalid_argument("X must be C-contiguous");
595 }
596 if (buf_x.ndim < 1 || buf_x.ndim > 2) {
597 throw std::invalid_argument("predict(): X must be 1 or 2-D array");
598 }
599 if (buf_x.ndim > 1 && buf_x.shape[1] != xcs.x_dim) {
600 std::ostringstream error;
601 error << "predict():";
602 error << " received x_dim: (" << buf_x.shape[1] << ")";
603 error << " but expected (" << xcs.x_dim << ")" << std::endl;
604 error << "Perhaps reshape your data.";
605 throw std::invalid_argument(error.str());
606 }
607 const int n_samples = buf_x.shape[0];
608 const double *input = reinterpret_cast<double *>(buf_x.ptr);
609 double *output =
610 (double *) malloc(sizeof(double) * n_samples * xcs.pa_size);
611 set_cover(cover);
612 xcs_supervised_predict(&xcs, input, output, n_samples, xcs.cover);
613 return py::array_t<double>(
614 std::vector<ptrdiff_t>{ n_samples, xcs.pa_size }, output);
615 }
616
626 double
627 score(const py::array_t<double> X, const py::array_t<double> Y, const int N,
628 const py::object &cover)
629 {
630 set_cover(cover);
631 load_input(test_data, X, Y);
632 if (N > 1) {
634 }
636 }
637
643 py::bytes
644 serialize() const
645 {
646 // Write XCSF to a temporary binary file
647 const char *filename = "_tmp_pickle.bin";
648 xcsf_save(&xcs, filename);
649 // Read the binary file into bytes
650 std::ifstream file(filename, std::ios::binary);
651 std::string state((std::istreambuf_iterator<char>(file)),
652 std::istreambuf_iterator<char>());
653 file.close();
654 // Delete the temporary file
655 if (std::remove(filename) != 0) {
656 perror("Error deleting temporary pickle file");
657 }
658 // Return the binary data as bytes
659 return py::bytes(state);
660 }
661
667 static XCS
668 deserialize(const py::bytes &state)
669 {
670 // Write the XCSF bytes to a temporary binary file
671 const char *filename = "_tmp_pickle.bin";
672 std::ofstream file(filename, std::ios::binary);
673 file.write(state.cast<std::string>().c_str(),
674 state.cast<std::string>().size());
675 file.close();
676 // Create a new XCSF instance
677 XCS xcs = XCS();
678 // Load XCSF
679 xcsf_load(&xcs.xcs, filename);
680 // Update object params
681 xcs.update_params();
682 // Delete the temporary file
683 if (std::remove(filename) != 0) {
684 perror("Error deleting temporary pickle file");
685 }
686 // Return the deserialized XCSF
687 return xcs;
688 }
689
690 /* GETTERS */
691
696 double
697 error(void)
698 {
699 return xcs.error;
700 }
701
702 py::dict
704 {
705 py::dict metrics;
706 metrics["train"] = metric_train;
707 metrics["val"] = metric_val;
708 metrics["trials"] = metric_trial;
709 metrics["psize"] = metric_psize;
710 metrics["msize"] = metric_msize;
711 metrics["mfrac"] = metric_mfrac;
712 return metrics;
713 }
714
715 int
717 {
718 return xcs.pset.size;
719 }
720
721 int
723 {
724 return xcs.pset.num;
725 }
726
727 int
729 {
730 return xcs.time;
731 }
732
733 double
735 {
736 return clset_mean_cond_size(&xcs, &xcs.pset);
737 }
738
739 double
741 {
742 return clset_mean_pred_size(&xcs, &xcs.pset);
743 }
744
745 double
746 get_pset_mean_pred_eta(const int layer)
747 {
748 return clset_mean_pred_eta(&xcs, &xcs.pset, layer);
749 }
750
751 double
753 {
754 return clset_mean_pred_neurons(&xcs, &xcs.pset, layer);
755 }
756
757 double
759 {
760 return clset_mean_pred_connections(&xcs, &xcs.pset, layer);
761 }
762
763 double
768
769 double
771 {
772 return clset_mean_cond_connections(&xcs, &xcs.pset, layer);
773 }
774
775 double
777 {
778 return clset_mean_cond_neurons(&xcs, &xcs.pset, layer);
779 }
780
781 double
786
787 double
789 {
790 return xcs.mset_size;
791 }
792
793 double
795 {
796 return xcs.aset_size;
797 }
798
799 double
801 {
802 return xcs.mfrac;
803 }
804
805 /* JSON */
806
814 std::string
815 json_export(const bool condition, const bool action, const bool prediction)
816 {
817 if (xcs.pset.list != NULL) {
818 return clset_json_export(&xcs, &xcs.pset, condition, action,
819 prediction);
820 }
821 return "null";
822 }
823
831 py::dict
832 get_pop(const bool condition, const bool action, const bool prediction)
833 {
834 std::string json_str = json_export(condition, action, prediction);
835 py::module_ json_module = py::module_::import("json");
836 py::object parsed_json = json_module.attr("loads")(json_str);
837 py::dict pop(parsed_json);
838 return pop;
839 }
840
844 void
846 {
847 char *json_str = param_json_export(&xcs);
848 py::module_ json_module = py::module_::import("json");
849 py::object parsed_json = json_module.attr("loads")(json_str);
850 py::dict result(parsed_json);
851 params = result;
852 // map None types
853 if (params.contains("random_state")) {
854 py::object rs = params["random_state"];
855 if (py::isinstance<py::int_>(rs) && rs.cast<long long>() < 0) {
856 params["random_state"] = py::none();
857 }
858 }
859 free(json_str);
860 }
861
867 py::dict
868 get_params(const bool deep)
869 {
870 (void) deep;
871 return params;
872 }
873
879 XCS &
880 set_params(py::kwargs kwargs)
881 {
882 py::dict kwargs_dict(kwargs);
883 // update external params dict
884 for (const auto &item : kwargs_dict) {
885 params[item.first] = item.second;
886 }
887 // map None types
888 if (kwargs_dict.contains("random_state")) {
889 py::object rs = kwargs["random_state"];
890 if (rs.is_none()) {
891 kwargs_dict["random_state"] = -1;
892 }
893 }
894 // convert dict to JSON and parse parameters
895 py::module_ json_module = py::module_::import("json");
896 py::object json_dumps = json_module.attr("dumps")(kwargs_dict);
897 std::string json_str = json_dumps.cast<std::string>();
898 const char *json_params = json_str.c_str();
899 param_json_import(&xcs, json_params);
900 return *this;
901 }
902
907 py::dict
909 {
910 char *json_str = param_json_export(&xcs);
911 py::module_ json_module = py::module_::import("json");
912 py::dict internal_params = json_module.attr("loads")(json_str);
913 free(json_str);
914 return internal_params;
915 }
916
921 void
922 insert_cl(const py::dict &classifier)
923 {
924 py::module_ json_module = py::module_::import("json");
925 py::object json_dumps = json_module.attr("dumps")(classifier);
926 std::string json_str = json_dumps.cast<std::string>();
927 cJSON *json = cJSON_Parse(json_str.c_str());
930 cJSON_Delete(json);
931 }
932
937 void
938 json_insert(const std::string &json_str)
939 {
940 clset_json_insert(&xcs, json_str.c_str());
941 }
942
947 void
948 json_write(const std::string &filename)
949 {
950 std::ofstream outfile(filename);
951 outfile << json_export(true, true, true);
952 outfile.close();
953 }
954
960 void
961 json_read(const std::string &filename, const bool clean)
962 {
963 if (clean) {
966 }
967 std::ifstream infile(filename);
968 std::stringstream buffer;
969 buffer << infile.rdbuf();
970 json_insert(buffer.str());
971 }
972};
973
975{
976 m.doc() = "XCSF learning classifier: rule-based online evolutionary "
977 "machine learning.\nFor details on how to use this module see: "
978 "https://github.com/xcsf-dev/xcsf/wiki/Python-Library-Usage";
979
980 double (XCS::*fit1)(const py::array_t<double>, const int, const double) =
981 &XCS::fit;
982 XCS &(XCS::*fit2)(const py::array_t<double>, const py::array_t<double>,
983 const bool, const bool, const bool, py::object,
984 py::kwargs) = &XCS::fit;
985
986 double (XCS::*error1)(void) = &XCS::error;
987 double (XCS::*error2)(const double, const bool, const double) = &XCS::error;
988
989 py::class_<Callback, std::unique_ptr<Callback, py::nodelete>>(m,
990 "Callback");
991
993 std::unique_ptr<EarlyStoppingCallback, py::nodelete>>(
994 m, "EarlyStoppingCallback")
995 .def(py::init<py::str, int, bool, double, int, bool>(),
996 "Creates a callback for terminating the fit function early.",
997 py::arg("monitor") = "train", py::arg("patience") = 0,
998 py::arg("restore_best") = false, py::arg("min_delta") = 0,
999 py::arg("start_from") = 0, py::arg("verbose") = true);
1000
1001 py::class_<CheckpointCallback, Callback,
1002 std::unique_ptr<CheckpointCallback, py::nodelete>>(
1003 m, "CheckpointCallback")
1004 .def(py::init<py::str, std::string, bool, int, bool>(),
1005 "Creates a callback for automatically saving XCSF.",
1006 py::arg("monitor") = "train", py::arg("filename") = "xcsf.bin",
1007 py::arg("save_best_only") = false, py::arg("save_freq") = 0,
1008 py::arg("verbose") = true);
1009
1010 py::class_<XCS>(m, "XCS")
1011 .def(py::init(), "Creates a new XCSF class with default arguments.")
1012 .def(py::init<py::kwargs>(),
1013 "Creates a new XCSF class with specified arguments.")
1014 .def("fit", fit1,
1015 "Creates/updates an action set for a given (state, action, "
1016 "reward). state shape must be: (x_dim, ).",
1017 py::arg("state"), py::arg("action"), py::arg("reward"))
1018 .def("fit", fit2,
1019 "Executes MAX_TRIALS number of XCSF learning iterations using the "
1020 "provided training data. X_train shape must be: (n_samples, "
1021 "x_dim). y_train shape must be: (n_samples, y_dim).",
1022 py::arg("X_train"), py::arg("y_train"), py::arg("shuffle") = true,
1023 py::arg("warm_start") = false, py::arg("verbose") = true,
1024 py::arg("callbacks") = py::none())
1025 .def(
1026 "score", &XCS::score,
1027 "Returns the error using at most N random samples from the "
1028 "provided data. N=0 uses all. X shape must be: (n_samples, x_dim). "
1029 "y shape must be: (n_samples, y_dim). If the match set is empty "
1030 "for a sample, the value of the cover array will be used "
1031 "otherwise zeros.",
1032 py::arg("X"), py::arg("y"), py::arg("N") = 0,
1033 py::arg("cover") = py::none())
1034 .def("error", error1,
1035 "Returns a moving average of the system error, updated with step "
1036 "size BETA.")
1037 .def("error", error2,
1038 "Returns the reinforcement learning system prediction error.",
1039 py::arg("reward"), py::arg("done"), py::arg("max_p"))
1040 .def("predict", &XCS::predict,
1041 "Returns the XCSF prediction array for the provided input. X "
1042 "shape must be: (n_samples, x_dim). Returns an array of shape: "
1043 "(n_samples, y_dim). If the match set is empty for a sample, the "
1044 "value of the cover array will be used, otherwise zeros. "
1045 "Cover must be an array of shape: y_dim.",
1046 py::arg("X"), py::arg("cover") = py::none())
1047 .def("save", &XCS::save,
1048 "Saves the current state of XCSF to persistent storage.",
1049 py::arg("filename"))
1050 .def("load", &XCS::load,
1051 "Loads the current state of XCSF from persistent storage.",
1052 py::arg("filename"))
1053 .def("store", &XCS::store,
1054 "Stores the current XCSF population in memory for later "
1055 "retrieval, overwriting any previously stored population.")
1056 .def("retrieve", &XCS::retrieve,
1057 "Retrieves the previously stored XCSF population from memory.")
1058 .def("init_trial", &XCS::init_trial, "Initialises a multi-step trial.")
1059 .def("end_trial", &XCS::end_trial, "Ends a multi-step trial.")
1060 .def("init_step", &XCS::init_step,
1061 "Initialises a step in a multi-step trial.")
1062 .def("end_step", &XCS::end_step, "Ends a step in a multi-step trial.")
1063 .def("decision", &XCS::decision,
1064 "Constructs the match set and selects an action to perform for "
1065 "reinforcement learning. state shape must be: (x_dim, )",
1066 py::arg("state"), py::arg("explore"))
1067 .def("update", &XCS::update,
1068 "Creates the action set using the previously selected action.",
1069 py::arg("reward"), py::arg("done"))
1070 .def("time", &XCS::get_time, "Returns the current EA time.")
1071 .def("get_metrics", &XCS::get_metrics,
1072 "Returns a dictionary of performance metrics.")
1073 .def("pset_size", &XCS::get_pset_size,
1074 "Returns the number of macro-classifiers in the population.")
1075 .def("pset_num", &XCS::get_pset_num,
1076 "Returns the number of micro-classifiers in the population.")
1077 .def("pset_mean_cond_size", &XCS::get_pset_mean_cond_size,
1078 "Returns the average condition size of classifiers in the "
1079 "population.")
1080 .def("pset_mean_pred_size", &XCS::get_pset_mean_pred_size,
1081 "Returns the average prediction size of classifiers in the "
1082 "population.")
1083 .def("pset_mean_pred_eta", &XCS::get_pset_mean_pred_eta,
1084 "Returns the mean eta for a prediction layer.", py::arg("layer"))
1085 .def("pset_mean_pred_neurons", &XCS::get_pset_mean_pred_neurons,
1086 "Returns the mean number of neurons for a prediction layer.",
1087 py::arg("layer"))
1088 .def("pset_mean_pred_layers", &XCS::get_pset_mean_pred_layers,
1089 "Returns the mean number of layers in the prediction networks.")
1090 .def("pset_mean_pred_connections", &XCS::get_pset_mean_pred_connections,
1091 "Returns the mean number of connections for a prediction layer.",
1092 py::arg("layer"))
1093 .def("pset_mean_cond_neurons", &XCS::get_pset_mean_cond_neurons,
1094 "Returns the mean number of neurons for a condition layer.",
1095 py::arg("layer"))
1096 .def("pset_mean_cond_layers", &XCS::get_pset_mean_cond_layers,
1097 "Returns the mean number of layers in the condition networks.")
1098 .def("pset_mean_cond_connections", &XCS::get_pset_mean_cond_connections,
1099 "Returns the mean number of connections for a condition layer.",
1100 py::arg("layer"))
1101 .def("mset_size", &XCS::get_mset_size,
1102 "Returns the average match set size.")
1103 .def("aset_size", &XCS::get_aset_size,
1104 "Returns the average action set size.")
1105 .def("mfrac", &XCS::get_mfrac,
1106 "Returns the mean fraction of inputs matched by the best rule.")
1107 .def("print_pset", &XCS::print_pset, "Prints the current population.",
1108 py::arg("condition") = true, py::arg("action") = true,
1109 py::arg("prediction") = true)
1110 .def("print_params", &XCS::print_params,
1111 "Prints the XCSF parameters and their current values.")
1112 .def("pred_expand", &XCS::pred_expand,
1113 "Inserts a new hidden layer before the output layer within all "
1114 "prediction neural networks in the population.")
1115 .def("ae_to_classifier", &XCS::ae_to_classifier,
1116 "Switches from autoencoding to classification.", py::arg("y_dim"),
1117 py::arg("n_del"))
1118 .def("json", &XCS::json_export,
1119 "Returns a JSON formatted string representing the population set.",
1120 py::arg("condition") = true, py::arg("action") = true,
1121 py::arg("prediction") = true)
1122 .def("json_write", &XCS::json_write,
1123 "Writes the current population set to a file in JSON.",
1124 py::arg("filename"))
1125 .def("json_read", &XCS::json_read,
1126 "Reads classifiers from a JSON file and adds to the population.",
1127 py::arg("filename"), py::arg("clean") = true)
1128 .def("get_params", &XCS::get_params, py::arg("deep") = true,
1129 "Returns a dictionary of parameters and their values.")
1130 .def("set_params", &XCS::set_params, "Sets parameters.")
1131 .def(
1132 "insert_cl", &XCS::insert_cl,
1133 "Creates a classifier from a dict and inserts into the population.",
1134 py::arg("classifier"))
1135 .def("json_insert", &XCS::json_insert,
1136 "Creates classifiers from JSON and inserts into the population.",
1137 py::arg("json_str"))
1138 .def("internal_params", &XCS::internal_params, "Gets internal params.")
1139 .def("get_pop", &XCS::get_pop,
1140 "Returns the current population as a dictionary.",
1141 py::arg("condition") = true, py::arg("action") = true,
1142 py::arg("prediction") = true)
1143 .def(py::pickle(
1144 [](const XCS &obj) { return obj.serialize(); },
1145 [](const py::bytes &state) { return XCS::deserialize(state); }));
1146}
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)
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.
XCS & set_params(py::kwargs kwargs)
Sets parameter values.
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.
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)
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 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.
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.
std::string json_export(const bool condition, const bool action, const bool prediction)
Returns a JSON formatted string representing the population set.
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.
void update_metrics(const double train, const double val, const int n_trials)
Updates performance metrics.
py::list metric_trial
void print_pset(const bool condition, const bool action, const bool prediction)
Prints the current population.
py::dict get_pop(const bool condition, const bool action, const bool prediction)
Returns a Python dictionary containing the population set.
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.
void json_read(const std::string &filename, const bool clean)
Reads classifiers from a JSON file and adds to the population.
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
int get_pset_num(void)
void update_params()
Updates the Python object's parameter dictionary.
py::dict params
Dictionary of parameters and their values.
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_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 * get_cover(const py::array_t< double > cover)
Returns the values specified in the cover array.
void insert_cl(const py::dict &classifier)
Creates a classifier from dict and inserts into the population.
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
void clset_kill(const struct XCSF *xcsf, struct Set *set)
Frees the set and the classifiers.
Definition clset.c:590
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
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_init(struct Set *set)
Initialises a new set.
Definition clset.c:328
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
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.
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 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.
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