Skip to content

Trainers

hgp_lib.trainers.gp_trainer.GPTrainer

High-level trainer for Boolean Genetic Programming. Accepts a TrainerConfig containing a BooleanGPConfig and training options. Runs the training loop and optionally validates every val_every epochs. Returns a HierarchicalHistory with GenerationMetrics per epoch.

Parameters:

Name Type Description Default
config TrainerConfig

Configuration with gp_config (BooleanGPConfig), num_epochs, optional val_data/val_labels, val_every, progress options.

required

Examples:

>>> import numpy as np
>>> from hgp_lib.configs import BooleanGPConfig, TrainerConfig
>>> from hgp_lib.trainers import GPTrainer
>>> from hgp_lib.utils.metrics import fast_accuracy_score as accuracy_score
>>>
>>> train_data = np.array([[True, False, True, False], [False, True, False, True]])
>>> train_labels = np.array([1, 0])
>>> val_data = np.array([[True, True, False, False]])
>>> val_labels = np.array([1])
>>> gp_config = BooleanGPConfig(
...     score_fn=accuracy_score,
...     train_data=train_data,
...     train_labels=train_labels,
...     optimize_scorer=False,
... )
>>> config = TrainerConfig(
...     gp_config=gp_config,
...     num_epochs=10,
...     val_data=val_data,
...     val_labels=val_labels,
...     val_every=5,
...     progress_bar=False,
... )
>>> trainer = GPTrainer(config)
>>> trainer_result_history = trainer.fit()
Source code in hgp_lib\trainers\gp_trainer.py
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
class GPTrainer:
    """
    High-level trainer for Boolean Genetic Programming.
    Accepts a TrainerConfig containing a BooleanGPConfig and training options.
    Runs the training loop and optionally validates every val_every epochs.
    Returns a HierarchicalHistory with GenerationMetrics per epoch.

    Args:
        config (TrainerConfig): Configuration with gp_config (BooleanGPConfig),
            num_epochs, optional val_data/val_labels, val_every, progress options.

    Examples:
        >>> import numpy as np
        >>> from hgp_lib.configs import BooleanGPConfig, TrainerConfig
        >>> from hgp_lib.trainers import GPTrainer
        >>> from hgp_lib.utils.metrics import fast_accuracy_score as accuracy_score
        >>>
        >>> train_data = np.array([[True, False, True, False], [False, True, False, True]])
        >>> train_labels = np.array([1, 0])
        >>> val_data = np.array([[True, True, False, False]])
        >>> val_labels = np.array([1])
        >>> gp_config = BooleanGPConfig(
        ...     score_fn=accuracy_score,
        ...     train_data=train_data,
        ...     train_labels=train_labels,
        ...     optimize_scorer=False,
        ... )
        >>> config = TrainerConfig(
        ...     gp_config=gp_config,
        ...     num_epochs=10,
        ...     val_data=val_data,
        ...     val_labels=val_labels,
        ...     val_every=5,
        ...     progress_bar=False,
        ... )
        >>> trainer = GPTrainer(config)
        >>> trainer_result_history = trainer.fit()
    """

    def __init__(self, config: TrainerConfig):
        validate_trainer_config(config)

        self.config = config
        self.gp_algo = BooleanGP(config.gp_config)
        self.num_epochs = config.num_epochs
        self.val_every = config.val_every
        self.progress_bar = config.progress_bar
        self.leave_progress_bar = config.leave_progress_bar
        self.progress_callback = config.progress_callback

        self.score_fn = self.gp_algo.score_fn  # Maybe optimized
        if config.val_data is not None and config.gp_config.optimize_scorer:
            self.val_score_fn, self.val_cm, self.val_data, self.val_labels = (
                optimize_scorers_for_data(
                    config.gp_config.score_fn,
                    confusion_matrix,
                    data=config.val_data,
                    labels=config.val_labels,
                )
            )
        else:
            self.val_score_fn = config.gp_config.score_fn
            self.val_cm = confusion_matrix
            self.val_data = config.val_data
            self.val_labels = config.val_labels

    def fit(self) -> PopulationHistory:
        """
        Trains the Boolean GP model for the specified number of epochs.
        Returns:
            HierarchicalHistory: History with parent and child population metrics.
        """
        parent_generations: List[GenerationMetrics] = []
        val_score = 0.0

        with tqdm(
            range(self.num_epochs),
            desc="Epochs",
            disable=not self.progress_bar,
            leave=self.leave_progress_bar,
        ) as tbar:
            for epoch in tbar:
                gen_metrics = self.gp_algo.step()

                # Get validation scores if validation data is available
                if self.val_data is not None and (
                    (epoch + 1) % self.val_every == 0 or epoch == self.num_epochs - 1
                ):
                    val_score = self.gp_algo.evaluate_best(
                        self.val_data,
                        self.val_labels,
                        self.val_score_fn,
                    )

                    gen_metrics.val_score = val_score

                parent_generations.append(gen_metrics)

                if (
                    self.progress_callback is not None
                    and (epoch + 1) % self.config.progress_update_interval == 0
                ):
                    self.progress_callback(self.config.progress_update_interval)

                tbar.set_postfix(
                    {
                        "train_best": f"{gen_metrics.best_train_score:.4f}",
                        "val_best": f"{val_score:.4f}",
                    }
                )

        # Send remaining epochs not covered by progress_update_interval
        remaining_epochs = self.num_epochs % self.config.progress_update_interval
        if remaining_epochs > 0 and self.progress_callback is not None:
            self.progress_callback(remaining_epochs)

        tp, fp, fn, tn = self.gp_algo.train_cm(
            self.gp_algo.train_labels,
            self.gp_algo.global_best_rule.evaluate(self.gp_algo.train_data),
        )
        val_tp, val_fp, val_fn, val_tn = None, None, None, None
        if self.val_data is not None:
            val_tp, val_fp, val_fn, val_tn = self.val_cm(
                self.val_labels, self.gp_algo.global_best_rule.evaluate(self.val_data)
            )
        return PopulationHistory(
            generations=parent_generations,
            tp=tp,
            fp=fp,
            fn=fn,
            tn=tn,
            val_tp=val_tp,
            val_fp=val_fp,
            val_fn=val_fn,
            val_tn=val_tn,
            global_best_rule=self.gp_algo.global_best_rule,
        )

    def predict(self, data: np.ndarray) -> np.ndarray:
        """
        Predict labels for ``data`` using the best rule found during training.

        This mirrors the scikit-learn ``predict`` API so a fitted ``GPTrainer`` can
        be dropped into places that expect an estimator. It must be called after
        ``fit``. The input must already be binarized (a boolean array) with the same
        feature layout as the training data.

        Args:
            data (np.ndarray):
                2-D boolean array of shape ``(n_samples, n_features)``, using the same
                binarized feature layout as the training data.

        Returns:
            np.ndarray: 1-D boolean array with one prediction per input row.

        Raises:
            RuntimeError: If called before ``fit`` (no best rule is available yet).

        Examples:
            >>> import numpy as np
            >>> from hgp_lib.configs import BooleanGPConfig, TrainerConfig
            >>> from hgp_lib.trainers import GPTrainer
            >>> from hgp_lib.utils.metrics import fast_accuracy_score as accuracy_score
            >>> train_data = np.array([[True, False], [False, True]])
            >>> train_labels = np.array([1, 0])
            >>> gp_config = BooleanGPConfig(
            ...     score_fn=accuracy_score,
            ...     train_data=train_data,
            ...     train_labels=train_labels,
            ...     optimize_scorer=False,
            ... )
            >>> config = TrainerConfig(gp_config=gp_config, num_epochs=5, progress_bar=False)
            >>> trainer = GPTrainer(config)
            >>> _ = trainer.fit()
            >>> predictions = trainer.predict(train_data)
            >>> predictions.shape
            (2,)
        """
        if self.gp_algo.global_best_rule is None:
            raise RuntimeError("GPTrainer must be fit before calling predict")
        return self.gp_algo.global_best_rule.evaluate(data)

fit()

Trains the Boolean GP model for the specified number of epochs. Returns: HierarchicalHistory: History with parent and child population metrics.

Source code in hgp_lib\trainers\gp_trainer.py
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
def fit(self) -> PopulationHistory:
    """
    Trains the Boolean GP model for the specified number of epochs.
    Returns:
        HierarchicalHistory: History with parent and child population metrics.
    """
    parent_generations: List[GenerationMetrics] = []
    val_score = 0.0

    with tqdm(
        range(self.num_epochs),
        desc="Epochs",
        disable=not self.progress_bar,
        leave=self.leave_progress_bar,
    ) as tbar:
        for epoch in tbar:
            gen_metrics = self.gp_algo.step()

            # Get validation scores if validation data is available
            if self.val_data is not None and (
                (epoch + 1) % self.val_every == 0 or epoch == self.num_epochs - 1
            ):
                val_score = self.gp_algo.evaluate_best(
                    self.val_data,
                    self.val_labels,
                    self.val_score_fn,
                )

                gen_metrics.val_score = val_score

            parent_generations.append(gen_metrics)

            if (
                self.progress_callback is not None
                and (epoch + 1) % self.config.progress_update_interval == 0
            ):
                self.progress_callback(self.config.progress_update_interval)

            tbar.set_postfix(
                {
                    "train_best": f"{gen_metrics.best_train_score:.4f}",
                    "val_best": f"{val_score:.4f}",
                }
            )

    # Send remaining epochs not covered by progress_update_interval
    remaining_epochs = self.num_epochs % self.config.progress_update_interval
    if remaining_epochs > 0 and self.progress_callback is not None:
        self.progress_callback(remaining_epochs)

    tp, fp, fn, tn = self.gp_algo.train_cm(
        self.gp_algo.train_labels,
        self.gp_algo.global_best_rule.evaluate(self.gp_algo.train_data),
    )
    val_tp, val_fp, val_fn, val_tn = None, None, None, None
    if self.val_data is not None:
        val_tp, val_fp, val_fn, val_tn = self.val_cm(
            self.val_labels, self.gp_algo.global_best_rule.evaluate(self.val_data)
        )
    return PopulationHistory(
        generations=parent_generations,
        tp=tp,
        fp=fp,
        fn=fn,
        tn=tn,
        val_tp=val_tp,
        val_fp=val_fp,
        val_fn=val_fn,
        val_tn=val_tn,
        global_best_rule=self.gp_algo.global_best_rule,
    )

predict(data)

Predict labels for data using the best rule found during training.

This mirrors the scikit-learn predict API so a fitted GPTrainer can be dropped into places that expect an estimator. It must be called after fit. The input must already be binarized (a boolean array) with the same feature layout as the training data.

Parameters:

Name Type Description Default
data ndarray

2-D boolean array of shape (n_samples, n_features), using the same binarized feature layout as the training data.

required

Returns:

Type Description
ndarray

np.ndarray: 1-D boolean array with one prediction per input row.

Raises:

Type Description
RuntimeError

If called before fit (no best rule is available yet).

Examples:

>>> import numpy as np
>>> from hgp_lib.configs import BooleanGPConfig, TrainerConfig
>>> from hgp_lib.trainers import GPTrainer
>>> from hgp_lib.utils.metrics import fast_accuracy_score as accuracy_score
>>> train_data = np.array([[True, False], [False, True]])
>>> train_labels = np.array([1, 0])
>>> gp_config = BooleanGPConfig(
...     score_fn=accuracy_score,
...     train_data=train_data,
...     train_labels=train_labels,
...     optimize_scorer=False,
... )
>>> config = TrainerConfig(gp_config=gp_config, num_epochs=5, progress_bar=False)
>>> trainer = GPTrainer(config)
>>> _ = trainer.fit()
>>> predictions = trainer.predict(train_data)
>>> predictions.shape
(2,)
Source code in hgp_lib\trainers\gp_trainer.py
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
def predict(self, data: np.ndarray) -> np.ndarray:
    """
    Predict labels for ``data`` using the best rule found during training.

    This mirrors the scikit-learn ``predict`` API so a fitted ``GPTrainer`` can
    be dropped into places that expect an estimator. It must be called after
    ``fit``. The input must already be binarized (a boolean array) with the same
    feature layout as the training data.

    Args:
        data (np.ndarray):
            2-D boolean array of shape ``(n_samples, n_features)``, using the same
            binarized feature layout as the training data.

    Returns:
        np.ndarray: 1-D boolean array with one prediction per input row.

    Raises:
        RuntimeError: If called before ``fit`` (no best rule is available yet).

    Examples:
        >>> import numpy as np
        >>> from hgp_lib.configs import BooleanGPConfig, TrainerConfig
        >>> from hgp_lib.trainers import GPTrainer
        >>> from hgp_lib.utils.metrics import fast_accuracy_score as accuracy_score
        >>> train_data = np.array([[True, False], [False, True]])
        >>> train_labels = np.array([1, 0])
        >>> gp_config = BooleanGPConfig(
        ...     score_fn=accuracy_score,
        ...     train_data=train_data,
        ...     train_labels=train_labels,
        ...     optimize_scorer=False,
        ... )
        >>> config = TrainerConfig(gp_config=gp_config, num_epochs=5, progress_bar=False)
        >>> trainer = GPTrainer(config)
        >>> _ = trainer.fit()
        >>> predictions = trainer.predict(train_data)
        >>> predictions.shape
        (2,)
    """
    if self.gp_algo.global_best_rule is None:
        raise RuntimeError("GPTrainer must be fit before calling predict")
    return self.gp_algo.global_best_rule.evaluate(data)

hgp_lib.trainers.boolean_rule_classifier.BooleanRuleClassifier

End-to-end classifier that binarizes raw tabular data and evolves a Boolean rule.

This is the easiest way to go from a raw pandas.DataFrame to a trained, human-readable rule. It owns a :class:Binarizer and a :class:GPTrainer: fit binarizes the raw features (label-aware) and evolves a rule on them, and predict binarizes new raw data with the same fitted binarizer before evaluating the rule. It mirrors the scikit-learn estimator API (fit/predict) so it can be dropped into places that expect an estimator.

Parameters:

Name Type Description Default
trainer_config TrainerConfig

Training configuration (epochs, scorer, evolutionary operators, ...). Its nested gp_config does not need train_data/train_labels; they are filled from the data passed to fit.

required
binarizer Binarizer | None

Binarizer used to turn raw features into boolean columns. When None (default), a :class:StandardBinarizer with default settings is used. The binarizer must not be already fitted.

None

Examples:

>>> from sklearn.datasets import load_breast_cancer
>>> from sklearn.model_selection import train_test_split
>>> from hgp_lib import BooleanRuleClassifier
>>> from hgp_lib.configs import BooleanGPConfig, TrainerConfig
>>> from hgp_lib.utils.metrics import fast_f1_score
>>> X, y = load_breast_cancer(return_X_y=True, as_frame=True)
>>> X_train, X_test, y_train, y_test = train_test_split(
...     X, y, test_size=0.2, stratify=y, random_state=0
... )
>>> X_train, X_val, y_train, y_val = train_test_split(
...     X_train, y_train, test_size=0.25, stratify=y_train, random_state=0
... )
>>> config = TrainerConfig(
...     gp_config=BooleanGPConfig(score_fn=fast_f1_score),
...     num_epochs=10,
...     val_every=5,
...     progress_bar=False,
... )
>>> clf = BooleanRuleClassifier(config)
>>> history = clf.fit(X_train, y_train, X_val, y_val)
>>> predictions = clf.predict(X_test)
>>> predictions.shape
(114,)
>>> history.best_val_score is not None
True
>>> isinstance(clf.format_rule(), str)
True
Source code in hgp_lib\trainers\boolean_rule_classifier.py
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
class BooleanRuleClassifier:
    """
    End-to-end classifier that binarizes raw tabular data and evolves a Boolean rule.

    This is the easiest way to go from a raw ``pandas.DataFrame`` to a trained,
    human-readable rule. It owns a :class:`Binarizer` and a :class:`GPTrainer`:
    ``fit`` binarizes the raw features (label-aware) and evolves a rule on them, and
    ``predict`` binarizes new raw data with the same fitted binarizer before evaluating
    the rule. It mirrors the scikit-learn estimator API (``fit``/``predict``) so it can
    be dropped into places that expect an estimator.

    Args:
        trainer_config (TrainerConfig):
            Training configuration (epochs, scorer, evolutionary operators, ...). Its
            nested ``gp_config`` does not need ``train_data``/``train_labels``; they are
            filled from the data passed to ``fit``.
        binarizer (Binarizer | None):
            Binarizer used to turn raw features into boolean columns. When ``None``
            (default), a :class:`StandardBinarizer` with default settings is used. The
            binarizer must not be already fitted.

    Examples:
        >>> from sklearn.datasets import load_breast_cancer
        >>> from sklearn.model_selection import train_test_split
        >>> from hgp_lib import BooleanRuleClassifier
        >>> from hgp_lib.configs import BooleanGPConfig, TrainerConfig
        >>> from hgp_lib.utils.metrics import fast_f1_score
        >>> X, y = load_breast_cancer(return_X_y=True, as_frame=True)
        >>> X_train, X_test, y_train, y_test = train_test_split(
        ...     X, y, test_size=0.2, stratify=y, random_state=0
        ... )
        >>> X_train, X_val, y_train, y_val = train_test_split(
        ...     X_train, y_train, test_size=0.25, stratify=y_train, random_state=0
        ... )
        >>> config = TrainerConfig(
        ...     gp_config=BooleanGPConfig(score_fn=fast_f1_score),
        ...     num_epochs=10,
        ...     val_every=5,
        ...     progress_bar=False,
        ... )
        >>> clf = BooleanRuleClassifier(config)
        >>> history = clf.fit(X_train, y_train, X_val, y_val)
        >>> predictions = clf.predict(X_test)
        >>> predictions.shape
        (114,)
        >>> history.best_val_score is not None
        True
        >>> isinstance(clf.format_rule(), str)
        True
    """

    def __init__(
        self, trainer_config: TrainerConfig, binarizer: Optional[Binarizer] = None
    ):
        validate_trainer_config(trainer_config, require_data=False)
        if binarizer is None:
            binarizer = StandardBinarizer()
        else:
            check_isinstance(binarizer, Binarizer)
            if binarizer.is_fitted:
                raise ValueError(
                    "binarizer must not be fitted before passing to BooleanRuleClassifier"
                )

        self.trainer_config = trainer_config
        self.binarizer = binarizer
        self._history: Optional[PopulationHistory] = None

    def fit(
        self,
        X: pd.DataFrame,
        y: "np.ndarray | pd.Series",
        X_val: Optional[pd.DataFrame] = None,
        y_val: "np.ndarray | pd.Series | None" = None,
    ) -> PopulationHistory:
        """
        Binarize raw features and evolve a Boolean rule on them.

        The binarizer is fitted on ``X`` (with labels ``y``, enabling supervised binning
        of numeric columns) and the resulting boolean matrix is used to train a
        :class:`GPTrainer`.

        Optionally, a raw validation set ``(X_val, y_val)`` can be supplied. It is
        transformed with the *same* fitted binarizer (no leakage) and used to track a
        validation score during training (every ``val_every`` epochs, as configured in
        the ``TrainerConfig``). The validation set passed here fully controls validation:
        when supplied it overrides any ``val_data`` already set on the trainer
        configuration, and when omitted any pre-existing ``val_data`` is cleared so no
        stale (and un-binarized) validation set leaks in.

        Args:
            X (pd.DataFrame): Raw (non-binarized) training features. Columns may be
                boolean, categorical, or numeric.
            y (np.ndarray | pd.Series): Binary training labels, one per row of ``X``.
            X_val (pd.DataFrame | None): Optional raw validation features, with the same
                schema as ``X``. Default: `None`.
            y_val (np.ndarray | pd.Series | None): Optional validation labels. Must be
                provided if and only if ``X_val`` is. Default: `None`.

        Returns:
            PopulationHistory: The training history, whose ``global_best_rule`` is the
                rule used for prediction. When validation data is supplied,
                ``history.best_val_score`` reports the best validation score.

        Raises:
            TypeError: If ``X`` (or ``X_val``, when given) is not a ``pandas.DataFrame``.
            ValueError: If exactly one of ``X_val`` / ``y_val`` is provided.
        """
        check_isinstance(X, pd.DataFrame)
        if (X_val is None) != (y_val is None):
            raise ValueError("X_val and y_val must both be provided or both be None")
        y = np.asarray(y)

        train_bin = self.binarizer.fit_transform(X, y).to_numpy(dtype=bool)
        gp_config = replace(
            self.trainer_config.gp_config, train_data=train_bin, train_labels=y
        )

        if X_val is not None:
            check_isinstance(X_val, pd.DataFrame)
            val_bin = self.binarizer.transform(X_val).to_numpy(dtype=bool)
            val_kwargs = {"val_data": val_bin, "val_labels": np.asarray(y_val)}
        else:
            # No validation set here: clear any val_data from the config so a stale,
            # un-binarized validation set can't leak into training.
            val_kwargs = {"val_data": None, "val_labels": None}

        fit_config = replace(self.trainer_config, gp_config=gp_config, **val_kwargs)

        self._history = GPTrainer(fit_config).fit()
        return self._history

    def predict(self, X: pd.DataFrame) -> np.ndarray:
        """
        Predict labels for raw ``X`` using the evolved rule.

        The raw features are binarized with the fitted binarizer, then the best rule
        found during ``fit`` is evaluated on them. Must be called after ``fit``.

        Args:
            X (pd.DataFrame): Raw features with the same columns, in the same order and
                dtypes, as the data used to fit.

        Returns:
            np.ndarray: 1-D boolean array with one prediction per input row.

        Raises:
            TypeError: If ``X`` is not a ``pandas.DataFrame``.
            RuntimeError: If called before ``fit``.
        """
        check_isinstance(X, pd.DataFrame)
        self._check_fitted("predict")
        data = self.binarizer.transform(X).to_numpy(dtype=bool)
        return self._history.global_best_rule.evaluate(data)

    @property
    def rule(self) -> Rule:
        """The best rule found during ``fit``. Requires a fitted classifier."""
        self._check_fitted("rule")
        return self._history.global_best_rule

    @property
    def feature_names(self) -> List[str]:
        """
        The binarized feature names in order (from the fitted binarizer), so that
        ``feature_names[i]`` names the feature a literal references with index ``i``.
        """
        self._check_fitted("feature_names")
        return self.binarizer.get_feature_names_out()

    def format_rule(self) -> str:
        """
        Return the evolved rule as a readable logical expression over the original
        binarized feature names, e.g. ``Or(mean radius < 15.0, ~worst area < 880.0)``.
        """
        return self.rule.to_str(self.feature_names)

    def _check_fitted(self, attr: str) -> None:
        if self._history is None:
            raise RuntimeError(
                f"BooleanRuleClassifier must be fit before accessing '{attr}'"
            )

rule property

The best rule found during fit. Requires a fitted classifier.

feature_names property

The binarized feature names in order (from the fitted binarizer), so that feature_names[i] names the feature a literal references with index i.

fit(X, y, X_val=None, y_val=None)

Binarize raw features and evolve a Boolean rule on them.

The binarizer is fitted on X (with labels y, enabling supervised binning of numeric columns) and the resulting boolean matrix is used to train a :class:GPTrainer.

Optionally, a raw validation set (X_val, y_val) can be supplied. It is transformed with the same fitted binarizer (no leakage) and used to track a validation score during training (every val_every epochs, as configured in the TrainerConfig). The validation set passed here fully controls validation: when supplied it overrides any val_data already set on the trainer configuration, and when omitted any pre-existing val_data is cleared so no stale (and un-binarized) validation set leaks in.

Parameters:

Name Type Description Default
X DataFrame

Raw (non-binarized) training features. Columns may be boolean, categorical, or numeric.

required
y ndarray | Series

Binary training labels, one per row of X.

required
X_val DataFrame | None

Optional raw validation features, with the same schema as X. Default: None.

None
y_val ndarray | Series | None

Optional validation labels. Must be provided if and only if X_val is. Default: None.

None

Returns:

Name Type Description
PopulationHistory PopulationHistory

The training history, whose global_best_rule is the rule used for prediction. When validation data is supplied, history.best_val_score reports the best validation score.

Raises:

Type Description
TypeError

If X (or X_val, when given) is not a pandas.DataFrame.

ValueError

If exactly one of X_val / y_val is provided.

Source code in hgp_lib\trainers\boolean_rule_classifier.py
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
def fit(
    self,
    X: pd.DataFrame,
    y: "np.ndarray | pd.Series",
    X_val: Optional[pd.DataFrame] = None,
    y_val: "np.ndarray | pd.Series | None" = None,
) -> PopulationHistory:
    """
    Binarize raw features and evolve a Boolean rule on them.

    The binarizer is fitted on ``X`` (with labels ``y``, enabling supervised binning
    of numeric columns) and the resulting boolean matrix is used to train a
    :class:`GPTrainer`.

    Optionally, a raw validation set ``(X_val, y_val)`` can be supplied. It is
    transformed with the *same* fitted binarizer (no leakage) and used to track a
    validation score during training (every ``val_every`` epochs, as configured in
    the ``TrainerConfig``). The validation set passed here fully controls validation:
    when supplied it overrides any ``val_data`` already set on the trainer
    configuration, and when omitted any pre-existing ``val_data`` is cleared so no
    stale (and un-binarized) validation set leaks in.

    Args:
        X (pd.DataFrame): Raw (non-binarized) training features. Columns may be
            boolean, categorical, or numeric.
        y (np.ndarray | pd.Series): Binary training labels, one per row of ``X``.
        X_val (pd.DataFrame | None): Optional raw validation features, with the same
            schema as ``X``. Default: `None`.
        y_val (np.ndarray | pd.Series | None): Optional validation labels. Must be
            provided if and only if ``X_val`` is. Default: `None`.

    Returns:
        PopulationHistory: The training history, whose ``global_best_rule`` is the
            rule used for prediction. When validation data is supplied,
            ``history.best_val_score`` reports the best validation score.

    Raises:
        TypeError: If ``X`` (or ``X_val``, when given) is not a ``pandas.DataFrame``.
        ValueError: If exactly one of ``X_val`` / ``y_val`` is provided.
    """
    check_isinstance(X, pd.DataFrame)
    if (X_val is None) != (y_val is None):
        raise ValueError("X_val and y_val must both be provided or both be None")
    y = np.asarray(y)

    train_bin = self.binarizer.fit_transform(X, y).to_numpy(dtype=bool)
    gp_config = replace(
        self.trainer_config.gp_config, train_data=train_bin, train_labels=y
    )

    if X_val is not None:
        check_isinstance(X_val, pd.DataFrame)
        val_bin = self.binarizer.transform(X_val).to_numpy(dtype=bool)
        val_kwargs = {"val_data": val_bin, "val_labels": np.asarray(y_val)}
    else:
        # No validation set here: clear any val_data from the config so a stale,
        # un-binarized validation set can't leak into training.
        val_kwargs = {"val_data": None, "val_labels": None}

    fit_config = replace(self.trainer_config, gp_config=gp_config, **val_kwargs)

    self._history = GPTrainer(fit_config).fit()
    return self._history

predict(X)

Predict labels for raw X using the evolved rule.

The raw features are binarized with the fitted binarizer, then the best rule found during fit is evaluated on them. Must be called after fit.

Parameters:

Name Type Description Default
X DataFrame

Raw features with the same columns, in the same order and dtypes, as the data used to fit.

required

Returns:

Type Description
ndarray

np.ndarray: 1-D boolean array with one prediction per input row.

Raises:

Type Description
TypeError

If X is not a pandas.DataFrame.

RuntimeError

If called before fit.

Source code in hgp_lib\trainers\boolean_rule_classifier.py
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
def predict(self, X: pd.DataFrame) -> np.ndarray:
    """
    Predict labels for raw ``X`` using the evolved rule.

    The raw features are binarized with the fitted binarizer, then the best rule
    found during ``fit`` is evaluated on them. Must be called after ``fit``.

    Args:
        X (pd.DataFrame): Raw features with the same columns, in the same order and
            dtypes, as the data used to fit.

    Returns:
        np.ndarray: 1-D boolean array with one prediction per input row.

    Raises:
        TypeError: If ``X`` is not a ``pandas.DataFrame``.
        RuntimeError: If called before ``fit``.
    """
    check_isinstance(X, pd.DataFrame)
    self._check_fitted("predict")
    data = self.binarizer.transform(X).to_numpy(dtype=bool)
    return self._history.global_best_rule.evaluate(data)

format_rule()

Return the evolved rule as a readable logical expression over the original binarized feature names, e.g. Or(mean radius < 15.0, ~worst area < 880.0).

Source code in hgp_lib\trainers\boolean_rule_classifier.py
185
186
187
188
189
190
def format_rule(self) -> str:
    """
    Return the evolved rule as a readable logical expression over the original
    binarized feature names, e.g. ``Or(mean radius < 15.0, ~worst area < 880.0)``.
    """
    return self.rule.to_str(self.feature_names)