XCSF 1.4.8
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 <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
41namespace py = pybind11;
42
43extern "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
64class 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:
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;
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);
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);
152 return s;
153 }
154
158 void
159 store(void)
160 {
162 }
163
167 void
169 {
171 }
172
176 void
178 {
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
257 {
259 }
260
264 void
266 {
268 }
269
273 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;
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 array contiguity
344 // https://github.com/pybind/pybind11/discussions/4211#discussioncomment-3905115
345 const int C_CONTIGUOUS =
346 py::detail::npy_api::constants::NPY_ARRAY_C_CONTIGUOUS_;
347 if (!(C_CONTIGUOUS == (X.flags() & C_CONTIGUOUS)) ||
348 !(C_CONTIGUOUS == (Y.flags() & C_CONTIGUOUS))) {
349 throw std::invalid_argument("X and Y must be C-contiguous");
350 }
351 // check input shape
352 if (buf_x.ndim < 1 || buf_x.ndim > 2) {
353 throw std::invalid_argument("X must be 1 or 2-D array");
354 }
355 if (buf_y.ndim < 1 || buf_y.ndim > 2) {
356 throw std::invalid_argument("Y must be 1 or 2-D array");
357 }
358 if (buf_x.shape[0] != buf_y.shape[0]) {
359 throw std::invalid_argument("X and Y n_samples are not equal");
360 }
361 if (buf_x.ndim > 1 && buf_x.shape[1] != xcs.x_dim) {
362 std::ostringstream error;
363 error << "load_input():";
364 error << " received x_dim: (" << buf_x.shape[1] << ")";
365 error << " but expected (" << xcs.x_dim << ")" << std::endl;
366 error << "Perhaps reshape your data.";
367 throw std::invalid_argument(error.str());
368 }
369 if (buf_y.ndim > 1 && buf_y.shape[1] != xcs.y_dim) {
370 std::ostringstream error;
371 error << "load_input():";
372 error << " received y_dim: (" << buf_y.shape[1] << ")";
373 error << " but expected (" << xcs.y_dim << ")" << std::endl;
374 error << "Perhaps reshape your data.";
375 throw std::invalid_argument(error.str());
376 }
377 // load input
378 data->n_samples = buf_x.shape[0];
379 data->x_dim = xcs.x_dim;
380 data->y_dim = xcs.y_dim;
381 data->x = static_cast<double *>(buf_x.ptr);
382 data->y = static_cast<double *>(buf_y.ptr);
383 }
384
388 void
390 {
391 double trial = py::cast<double>(metric_trial[metric_trial.size() - 1]);
392 double train = py::cast<double>(metric_train[metric_train.size() - 1]);
393 double psize = py::cast<double>(metric_psize[metric_psize.size() - 1]);
394 double msize = py::cast<double>(metric_msize[metric_msize.size() - 1]);
395 double mfrac = py::cast<double>(metric_mfrac[metric_mfrac.size() - 1]);
396 std::ostringstream status;
397 status << get_timestamp();
398 status << " trials=" << trial;
399 status << " train=" << std::fixed << std::setprecision(5) << train;
400 if (val_data != NULL) {
401 double val = py::cast<double>(metric_val[metric_val.size() - 1]);
402 status << " val=" << std::fixed << std::setprecision(5) << val;
403 }
404 status << " pset=" << std::fixed << std::setprecision(1) << psize;
405 status << " mset=" << std::fixed << std::setprecision(1) << msize;
406 status << " mfrac=" << std::fixed << std::setprecision(2) << mfrac;
407 py::print(status.str());
408 }
409
416 void
417 update_metrics(const double train, const double val, const int n_trials)
418 {
419 const int trial = (1 + metric_counter) * n_trials;
420 metric_train.append(train);
421 metric_val.append(val);
422 metric_trial.append(trial);
423 metric_psize.append(xcs.pset.size);
424 metric_msize.append(xcs.mset_size);
425 metric_mfrac.append(xcs.mfrac);
427 }
428
433 void
434 load_validation_data(py::kwargs kwargs)
435 {
436 val_data = NULL;
437 if (kwargs.contains("validation_data")) {
438 py::tuple data = kwargs["validation_data"].cast<py::tuple>();
439 if (data) {
440 if (data.size() != 2) {
441 throw std::invalid_argument(
442 "validation_data must be a tuple with two arrays");
443 }
444 py::array_t<double> X_val = data[0].cast<py::array_t<double>>();
445 py::array_t<double> y_val = data[1].cast<py::array_t<double>>();
446 load_input(test_data, X_val, y_val);
448 // use zeros for validation predictions instead of covering
449 memset(xcs.cover, 0, sizeof(double) * xcs.pa_size);
450 }
451 }
452 }
453
459 bool
460 callbacks_run(py::list callbacks)
461 {
462 bool terminate = false;
463 py::dict metrics = get_metrics();
464 for (py::handle item : callbacks) {
465 if (py::isinstance<Callback>(item)) {
466 Callback *cb = py::cast<Callback *>(item);
467 if (cb->run(&xcs, metrics)) {
468 terminate = true;
469 }
470 } else {
471 throw std::invalid_argument("unsupported callback");
472 }
473 }
474 return terminate;
475 }
476
481 void
482 callbacks_finish(py::list callbacks)
483 {
484 for (py::handle item : callbacks) {
485 if (py::isinstance<Callback>(item)) {
486 Callback *cb = py::cast<Callback *>(item);
487 cb->finish(&xcs);
488 } else {
489 throw std::invalid_argument("unsupported callback");
490 }
491 }
492 }
493
506 XCS &
507 fit(const py::array_t<double> X_train, const py::array_t<double> y_train,
508 const bool shuffle, const bool warm_start, const bool verbose,
509 py::object callbacks, py::kwargs kwargs)
510 {
511 if (!warm_start) { // re-initialise XCSF as necessary
512 xcsf_free(&xcs);
513 xcsf_init(&xcs);
514 }
515 load_input(train_data, X_train, y_train);
516 load_validation_data(kwargs);
517 // get callbacks
518 py::list calls;
519 if (py::isinstance<py::list>(callbacks)) {
520 calls = callbacks.cast<py::list>();
521 }
522 // break up the learning into epochs to track metrics
523 const int n = ceil(xcs.MAX_TRIALS / (double) xcs.PERF_TRIALS);
524 const int n_trials = std::min(xcs.MAX_TRIALS, xcs.PERF_TRIALS);
525 for (int i = 0; i < n; ++i) {
526 const int start = i * n_trials;
527 const double train_error = xcs_supervised_fit(
528 &xcs, train_data, NULL, shuffle, start, n_trials);
529 double val_error = 0;
530 if (val_data != NULL) {
531 val_error = xcs_supervised_score(&xcs, val_data, xcs.cover);
532 }
533 update_metrics(train_error, val_error, n_trials);
534 if (verbose) {
535 print_status();
536 }
537 if (callbacks_run(calls)) {
538 break;
539 }
540 }
541 callbacks_finish(calls);
542 return *this;
543 }
544
550 double *
551 get_cover(const py::array_t<double> cover)
552 {
553 const py::buffer_info buf_c = cover.request();
554 if (buf_c.ndim != 1) {
555 std::ostringstream err;
556 err << "cover must be an array of shape (1, " << xcs.y_dim << ")"
557 << std::endl;
558 throw std::invalid_argument(err.str());
559 }
560 if (buf_c.shape[0] != xcs.y_dim) {
561 std::ostringstream err;
562 err << "cover length = " << buf_c.shape[0] << " but expected "
563 << xcs.y_dim << std::endl;
564 throw std::invalid_argument(err.str());
565 }
566 return reinterpret_cast<double *>(buf_c.ptr);
567 }
568
573 void
574 set_cover(const py::object &cover)
575 {
576 if (cover.is_none()) {
577 memset(xcs.cover, 0, sizeof(double) * xcs.pa_size);
578 } else {
579 py::array_t<double> cover_arr = cover.cast<py::array_t<double>>();
580 xcs.cover = get_cover(cover_arr);
581 }
582 }
583
591 py::array_t<double>
592 predict(const py::array_t<double> X, const py::object &cover)
593 {
594 const py::buffer_info buf_x = X.request();
595 // check array contiguity
596 // https://github.com/pybind/pybind11/discussions/4211#discussioncomment-3905115
597 const int C_CONTIGUOUS =
598 py::detail::npy_api::constants::NPY_ARRAY_C_CONTIGUOUS_;
599 if (!(C_CONTIGUOUS == (X.flags() & C_CONTIGUOUS))) {
600 throw std::invalid_argument("X must be C-contiguous");
601 }
602 if (buf_x.ndim < 1 || buf_x.ndim > 2) {
603 throw std::invalid_argument("predict(): X must be 1 or 2-D array");
604 }
605 if (buf_x.ndim > 1 && buf_x.shape[1] != xcs.x_dim) {
606 std::ostringstream error;
607 error << "predict():";
608 error << " received x_dim: (" << buf_x.shape[1] << ")";
609 error << " but expected (" << xcs.x_dim << ")" << std::endl;
610 error << "Perhaps reshape your data.";
611 throw std::invalid_argument(error.str());
612 }
613 const int n_samples = buf_x.shape[0];
614 const double *input = reinterpret_cast<double *>(buf_x.ptr);
615 double *output =
616 (double *) malloc(sizeof(double) * n_samples * xcs.pa_size);
617 set_cover(cover);
618 xcs_supervised_predict(&xcs, input, output, n_samples, xcs.cover);
619 return py::array_t<double>(
620 std::vector<ptrdiff_t>{ n_samples, xcs.pa_size }, output);
621 }
622
632 double
633 score(const py::array_t<double> X, const py::array_t<double> Y, const int N,
634 const py::object &cover)
635 {
636 set_cover(cover);
637 load_input(test_data, X, Y);
638 if (N > 1) {
640 }
642 }
643
649 py::bytes
650 serialize() const
651 {
652 // Write XCSF to a temporary binary file
653 const char *filename = "_tmp_pickle.bin";
654 xcsf_save(&xcs, filename);
655 // Read the binary file into bytes
656 std::ifstream file(filename, std::ios::binary);
657 std::string state((std::istreambuf_iterator<char>(file)),
658 std::istreambuf_iterator<char>());
659 file.close();
660 // Delete the temporary file
661 if (std::remove(filename) != 0) {
662 perror("Error deleting temporary pickle file");
663 }
664 // Return the binary data as bytes
665 return py::bytes(state);
666 }
667
673 static XCS
674 deserialize(const py::bytes &state)
675 {
676 // Write the XCSF bytes to a temporary binary file
677 const char *filename = "_tmp_pickle.bin";
678 std::ofstream file(filename, std::ios::binary);
679 file.write(state.cast<std::string>().c_str(),
680 state.cast<std::string>().size());
681 file.close();
682 // Create a new XCSF instance
683 XCS xcs = XCS();
684 // Load XCSF
685 xcsf_load(&xcs.xcs, filename);
686 // Update object params
687 xcs.update_params();
688 // Delete the temporary file
689 if (std::remove(filename) != 0) {
690 perror("Error deleting temporary pickle file");
691 }
692 // Return the deserialized XCSF
693 return xcs;
694 }
695
696 /* GETTERS */
697
702 double
703 error(void)
704 {
705 return xcs.error;
706 }
707
708 py::dict
710 {
711 py::dict metrics;
712 metrics["train"] = metric_train;
713 metrics["val"] = metric_val;
714 metrics["trials"] = metric_trial;
715 metrics["psize"] = metric_psize;
716 metrics["msize"] = metric_msize;
717 metrics["mfrac"] = metric_mfrac;
718 return metrics;
719 }
720
721 int
723 {
724 return xcs.pset.size;
725 }
726
727 int
729 {
730 return xcs.pset.num;
731 }
732
733 int
735 {
736 return xcs.time;
737 }
738
739 double
741 {
742 return clset_mean_cond_size(&xcs, &xcs.pset);
743 }
744
745 double
747 {
748 return clset_mean_pred_size(&xcs, &xcs.pset);
749 }
750
751 double
752 get_pset_mean_pred_eta(const int layer)
753 {
754 return clset_mean_pred_eta(&xcs, &xcs.pset, layer);
755 }
756
757 double
759 {
760 return clset_mean_pred_neurons(&xcs, &xcs.pset, layer);
761 }
762
763 double
765 {
766 return clset_mean_pred_connections(&xcs, &xcs.pset, layer);
767 }
768
769 double
774
775 double
777 {
778 return clset_mean_cond_connections(&xcs, &xcs.pset, layer);
779 }
780
781 double
783 {
784 return clset_mean_cond_neurons(&xcs, &xcs.pset, layer);
785 }
786
787 double
792
793 double
795 {
796 return xcs.mset_size;
797 }
798
799 double
801 {
802 return xcs.aset_size;
803 }
804
805 double
807 {
808 return xcs.mfrac;
809 }
810
811 /* JSON */
812
820 const char *
821 json_export(const bool condition, const bool action, const bool prediction)
822 {
823 if (xcs.pset.list != NULL) {
824 return clset_json_export(&xcs, &xcs.pset, condition, action,
825 prediction);
826 }
827 return "null";
828 }
829
833 void
835 {
836 char *json_str = param_json_export(&xcs);
837 py::module json = py::module::import("json");
838 py::object parsed_json = json.attr("loads")(json_str);
839 py::dict result(parsed_json);
840 params = result;
841 // map None types
842 if (params.contains("random_state")) {
843 py::object rs = params["random_state"];
844 if (py::isinstance<py::int_>(rs) && rs.cast<long long>() < 0) {
845 params["random_state"] = py::none();
846 }
847 }
848 free(json_str);
849 }
850
856 py::dict
857 get_params(const bool deep)
858 {
859 (void) deep;
860 return params;
861 }
862
868 XCS &
869 set_params(py::kwargs kwargs)
870 {
871 py::dict kwargs_dict(kwargs);
872 // update external params dict
873 for (const auto &item : kwargs_dict) {
874 params[item.first] = item.second;
875 }
876 // map None types
877 if (kwargs_dict.contains("random_state")) {
878 py::object rs = kwargs["random_state"];
879 if (rs.is_none()) {
880 kwargs_dict["random_state"] = -1;
881 }
882 }
883 // convert dict to JSON and parse parameters
884 py::module json_module = py::module::import("json");
885 py::object json_dumps = json_module.attr("dumps")(kwargs_dict);
886 std::string json_str = json_dumps.cast<std::string>();
887 const char *json_params = json_str.c_str();
888 param_json_import(&xcs, json_params);
889 return *this;
890 }
891
896 py::dict
898 {
899 char *json_str = param_json_export(&xcs);
900 py::module json_module = py::module::import("json");
901 py::dict internal_params = json_module.attr("loads")(json_str);
902 free(json_str);
903 return internal_params;
904 }
905
910 void
911 json_insert_cl(const std::string &json_str)
912 {
913 cJSON *json = cJSON_Parse(json_str.c_str());
916 cJSON_Delete(json);
917 }
918
923 void
924 json_insert(const std::string &json_str)
925 {
926 clset_json_insert(&xcs, json_str.c_str());
927 }
928
933 void
934 json_write(const std::string &filename)
935 {
936 std::ofstream outfile(filename);
937 outfile << json_export(true, true, true);
938 outfile.close();
939 }
940
946 void
947 json_read(const std::string &filename, const bool clean)
948 {
949 if (clean) {
952 }
953 std::ifstream infile(filename);
954 std::stringstream buffer;
955 buffer << infile.rdbuf();
956 json_insert(buffer.str());
957 }
958};
959
961{
962 m.doc() = "XCSF learning classifier: rule-based online evolutionary "
963 "machine learning.\nFor details on how to use this module see: "
964 "https://github.com/xcsf-dev/xcsf/wiki/Python-Library-Usage";
965
966 double (XCS::*fit1)(const py::array_t<double>, const int, const double) =
967 &XCS::fit;
968 XCS &(XCS::*fit2)(const py::array_t<double>, const py::array_t<double>,
969 const bool, const bool, const bool, py::object,
970 py::kwargs) = &XCS::fit;
971
972 double (XCS::*error1)(void) = &XCS::error;
973 double (XCS::*error2)(const double, const bool, const double) = &XCS::error;
974
975 py::class_<Callback, std::unique_ptr<Callback, py::nodelete>>(m,
976 "Callback");
977
979 std::unique_ptr<EarlyStoppingCallback, py::nodelete>>(
980 m, "EarlyStoppingCallback")
981 .def(py::init<py::str, int, bool, double, int, bool>(),
982 "Creates a callback for terminating the fit function early.",
983 py::arg("monitor") = "train", py::arg("patience") = 0,
984 py::arg("restore_best") = false, py::arg("min_delta") = 0,
985 py::arg("start_from") = 0, py::arg("verbose") = true);
986
987 py::class_<CheckpointCallback, Callback,
988 std::unique_ptr<CheckpointCallback, py::nodelete>>(
989 m, "CheckpointCallback")
990 .def(py::init<py::str, std::string, bool, int, bool>(),
991 "Creates a callback for automatically saving XCSF.",
992 py::arg("monitor") = "train", py::arg("filename") = "xcsf.bin",
993 py::arg("save_best_only") = false, py::arg("save_freq") = 0,
994 py::arg("verbose") = true);
995
996 py::class_<XCS>(m, "XCS")
997 .def(py::init(), "Creates a new XCSF class with default arguments.")
998 .def(py::init<py::kwargs>(),
999 "Creates a new XCSF class with specified arguments.")
1000 .def("fit", fit1,
1001 "Creates/updates an action set for a given (state, action, "
1002 "reward). state shape must be: (x_dim, ).",
1003 py::arg("state"), py::arg("action"), py::arg("reward"))
1004 .def("fit", fit2,
1005 "Executes MAX_TRIALS number of XCSF learning iterations using the "
1006 "provided training data. X_train shape must be: (n_samples, "
1007 "x_dim). y_train shape must be: (n_samples, y_dim).",
1008 py::arg("X_train"), py::arg("y_train"), py::arg("shuffle") = true,
1009 py::arg("warm_start") = false, py::arg("verbose") = true,
1010 py::arg("callbacks") = py::none())
1011 .def(
1012 "score", &XCS::score,
1013 "Returns the error using at most N random samples from the "
1014 "provided data. N=0 uses all. X shape must be: (n_samples, x_dim). "
1015 "y shape must be: (n_samples, y_dim). If the match set is empty "
1016 "for a sample, the value of the cover array will be used "
1017 "otherwise zeros.",
1018 py::arg("X"), py::arg("y"), py::arg("N") = 0,
1019 py::arg("cover") = py::none())
1020 .def("error", error1,
1021 "Returns a moving average of the system error, updated with step "
1022 "size BETA.")
1023 .def("error", error2,
1024 "Returns the reinforcement learning system prediction error.",
1025 py::arg("reward"), py::arg("done"), py::arg("max_p"))
1026 .def("predict", &XCS::predict,
1027 "Returns the XCSF prediction array for the provided input. X "
1028 "shape must be: (n_samples, x_dim). Returns an array of shape: "
1029 "(n_samples, y_dim). If the match set is empty for a sample, the "
1030 "value of the cover array will be used, otherwise zeros. "
1031 "Cover must be an array of shape: y_dim.",
1032 py::arg("X"), py::arg("cover") = py::none())
1033 .def("save", &XCS::save,
1034 "Saves the current state of XCSF to persistent storage.",
1035 py::arg("filename"))
1036 .def("load", &XCS::load,
1037 "Loads the current state of XCSF from persistent storage.",
1038 py::arg("filename"))
1039 .def("store", &XCS::store,
1040 "Stores the current XCSF population in memory for later "
1041 "retrieval, overwriting any previously stored population.")
1042 .def("retrieve", &XCS::retrieve,
1043 "Retrieves the previously stored XCSF population from memory.")
1044 .def("init_trial", &XCS::init_trial, "Initialises a multi-step trial.")
1045 .def("end_trial", &XCS::end_trial, "Ends a multi-step trial.")
1046 .def("init_step", &XCS::init_step,
1047 "Initialises a step in a multi-step trial.")
1048 .def("end_step", &XCS::end_step, "Ends a step in a multi-step trial.")
1049 .def("decision", &XCS::decision,
1050 "Constructs the match set and selects an action to perform for "
1051 "reinforcement learning. state shape must be: (x_dim, )",
1052 py::arg("state"), py::arg("explore"))
1053 .def("update", &XCS::update,
1054 "Creates the action set using the previously selected action.",
1055 py::arg("reward"), py::arg("done"))
1056 .def("time", &XCS::get_time, "Returns the current EA time.")
1057 .def("get_metrics", &XCS::get_metrics,
1058 "Returns a dictionary of performance metrics.")
1059 .def("pset_size", &XCS::get_pset_size,
1060 "Returns the number of macro-classifiers in the population.")
1061 .def("pset_num", &XCS::get_pset_num,
1062 "Returns the number of micro-classifiers in the population.")
1063 .def("pset_mean_cond_size", &XCS::get_pset_mean_cond_size,
1064 "Returns the average condition size of classifiers in the "
1065 "population.")
1066 .def("pset_mean_pred_size", &XCS::get_pset_mean_pred_size,
1067 "Returns the average prediction size of classifiers in the "
1068 "population.")
1069 .def("pset_mean_pred_eta", &XCS::get_pset_mean_pred_eta,
1070 "Returns the mean eta for a prediction layer.", py::arg("layer"))
1071 .def("pset_mean_pred_neurons", &XCS::get_pset_mean_pred_neurons,
1072 "Returns the mean number of neurons for a prediction layer.",
1073 py::arg("layer"))
1074 .def("pset_mean_pred_layers", &XCS::get_pset_mean_pred_layers,
1075 "Returns the mean number of layers in the prediction networks.")
1076 .def("pset_mean_pred_connections", &XCS::get_pset_mean_pred_connections,
1077 "Returns the mean number of connections for a prediction layer.",
1078 py::arg("layer"))
1079 .def("pset_mean_cond_neurons", &XCS::get_pset_mean_cond_neurons,
1080 "Returns the mean number of neurons for a condition layer.",
1081 py::arg("layer"))
1082 .def("pset_mean_cond_layers", &XCS::get_pset_mean_cond_layers,
1083 "Returns the mean number of layers in the condition networks.")
1084 .def("pset_mean_cond_connections", &XCS::get_pset_mean_cond_connections,
1085 "Returns the mean number of connections for a condition layer.",
1086 py::arg("layer"))
1087 .def("mset_size", &XCS::get_mset_size,
1088 "Returns the average match set size.")
1089 .def("aset_size", &XCS::get_aset_size,
1090 "Returns the average action set size.")
1091 .def("mfrac", &XCS::get_mfrac,
1092 "Returns the mean fraction of inputs matched by the best rule.")
1093 .def("print_pset", &XCS::print_pset, "Prints the current population.",
1094 py::arg("condition") = true, py::arg("action") = true,
1095 py::arg("prediction") = true)
1096 .def("print_params", &XCS::print_params,
1097 "Prints the XCSF parameters and their current values.")
1098 .def("pred_expand", &XCS::pred_expand,
1099 "Inserts a new hidden layer before the output layer within all "
1100 "prediction neural networks in the population.")
1101 .def("ae_to_classifier", &XCS::ae_to_classifier,
1102 "Switches from autoencoding to classification.", py::arg("y_dim"),
1103 py::arg("n_del"))
1104 .def("json", &XCS::json_export,
1105 "Returns a JSON formatted string representing the population set.",
1106 py::arg("condition") = true, py::arg("action") = true,
1107 py::arg("prediction") = true)
1108 .def("json_write", &XCS::json_write,
1109 "Writes the current population set to a file in JSON.",
1110 py::arg("filename"))
1111 .def("json_read", &XCS::json_read,
1112 "Reads classifiers from a JSON file and adds to the population.",
1113 py::arg("filename"), py::arg("clean") = true)
1114 .def("get_params", &XCS::get_params, py::arg("deep") = true,
1115 "Returns a dictionary of parameters and their values.")
1116 .def("set_params", &XCS::set_params, "Sets parameters.")
1117 .def("json_insert_cl", &XCS::json_insert_cl,
1118 "Creates a classifier from JSON and inserts into the population.",
1119 py::arg("json_str"))
1120 .def("json_insert", &XCS::json_insert,
1121 "Creates classifiers from JSON and inserts into the population.",
1122 py::arg("json_str"))
1123 .def("internal_params", &XCS::internal_params, "Gets internal params.")
1124 .def(py::pickle(
1125 [](const XCS &obj) { return obj.serialize(); },
1126 [](const py::bytes &state) { return XCS::deserialize(state); }));
1127}
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)
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.
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.
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.
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)
const char * json_export(const bool condition, const bool action, const bool prediction)
Returns a JSON formatted string representing the population set.
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.
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.
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.
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.
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