Skip to content

WIP, ENH: Add support for gamma scaling and iterative solvers. - #70

Open
nray wants to merge 9 commits into
mainfrom
nray/gamma_scaling
Open

WIP, ENH: Add support for gamma scaling and iterative solvers.#70
nray wants to merge 9 commits into
mainfrom
nray/gamma_scaling

Conversation

@nray

@nray nray commented Mar 19, 2026

Copy link
Copy Markdown
Collaborator

This draft MR contains the changes needed for gamma scaling experiments.

Addresses issue: #71

@nray nray self-assigned this Mar 19, 2026
Comment thread src/gfdl/model.py Outdated
return out


class GFDLRegressor(RegressorMixin, MultiOutputMixin, GFDL):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Has something changed about GFDLRegressor? The diff seems quite messy here, making it hard to judge what has actually changed and what hasn't. This class was deleted below and then pasted here?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the class was at the end of the file, but I moved it after GFDLClassifier to be have similar estimators one after the other. The only other change is the extra argument "gamma" to the constructor and "solver" to fit method.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be best not to reorganize the classes for now I think, so we can focus on what actually changed in the diff.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reverted to the old organization.

Comment thread src/gfdl/model.py Outdated
return np.random.default_rng(seed)


def stochastic_gradient_descent(self, X, y):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the motivation for rolling our own SGD instead of using upstream SGDRegressor from sklearn, which has learning_rate, eta0, etc.? Does its verbose argument not provide enough output?

You mention it briefly in the matching issue, but my intuition would always be to try the upstream/well-tested solution first before investing time on a hand-rolled version.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created a sub issue.

Comment thread src/gfdl/model.py Outdated

return self

def gradient_descent(self, X, y):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would also be good to clarify why we can't use i.e., scipy.optimize.minimize (which has some output options and custom callback support), and if we ever need mini-batch/partial_fit() support for these experiments or not.

@tylerjereddy tylerjereddy added the enhancement New feature or request label Mar 27, 2026
Comment thread src/gfdl/model.py Outdated
seed: int = None,
reg_alpha: float = None,
rtol: float | None = None,
gamma: float = None,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kostas' email on March 29/2026 suggests that there may be interest in the ability to provide different scaling values for different layers and/or to only apply scaling to a subset of layers, so the API design may need careful consideration.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The api can now pass different gamma for each layer.

@tylerjereddy

Copy link
Copy Markdown
Collaborator

Beyond the various cleanups and clarifications noted above, this will also need regression tests before it is seriously ready for review.

Given the amount of confusion I've seen around gamma scaling, the documentation should also explain how it works with crystal clarity.

Comment thread src/gfdl/model.py Outdated
Z = H_prev @ w.T + b # (n_samples, n_hidden)
H_prev = self._activation_fn(Z)
if self.gamma is not None:
H_prev *= 1.0 / (H_prev.shape[1] ** self.gamma)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@shahyadk-bu, @eiviani-lanl : I would appreciate some feedback about the gamma scaling api implemented here. To my understanding, this is equivalent to Shahyad's implementation here, but still it would nice to get a confirmation from both of you.

@shahyadk-bu

shahyadk-bu commented Apr 8, 2026 via email

Copy link
Copy Markdown

@nray

nray commented Apr 9, 2026

Copy link
Copy Markdown
Collaborator Author

Hi Ray, This seems correct. I also have begun using the non-training method of solving the RVFL directly and have that code in my Git as well if you wanted to look at that reference as well. It is the folder called "RVFL_Solved" on my Git and there is a python script called "RVFL_model.py" which holds my RVFL class and the scaling is implemented there as well. Sincerely, Shahyad

On Wed, Apr 8, 2026 at 4:23 AM Navamita Ray @.> wrote: @.* commented on this pull request. ------------------------------ In src/gfdl/model.py <#70 (comment)>: > Z = H_prev @ w.T + b # (n_samples, n_hidden) H_prev = self._activation_fn(Z) + if self.gamma is not None: + H_prev = 1.0 / (H_prev.shape[1] ** self.gamma) @shahyadk-bu https://github.com/shahyadk-bu, @eiviani-lanl https://github.com/eiviani-lanl : I would appreciate some feedback about the gamma scaling api implemented here. To my understanding, this is equivalent to Shahyad's implementation here https://github.com/shahyadk-bu/RVFL_Research/blob/main/RVFL_Trained/RVFL_Model.py#L45, but still it would nice to get a confirmation from both of you. — Reply to this email directly, view it on GitHub <#70 (review)>, or unsubscribe https://github.com/notifications/unsubscribe-auth/BZ2CZWVMIEDQOE5CSISBSJD4UWZ3ZAVCNFSM6AAAAACWYC7PESVHI2DSMVQWIX3LMV43YUDVNRWFEZLROVSXG5CSMV3GSZLXHM2DANZSGQZDENRXGY . You are receiving this because you were mentioned.Message ID: @.**>

Hi Shahyad,
Thanks for the confirmation, I will also take a look at your RVFL implementation that you pointed to.

Comment thread src/gfdl/model.py Outdated
seed: int = None,
reg_alpha: float = None,
rtol: float | None = None,
gamma: float | np.typing.ArrayLike | None = None,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here and elsewhere, note that ArrayLike already includes floats and other scalars: https://numpy.org/devdocs/reference/typing.html#numpy.typing.ArrayLike

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

Comment thread src/gfdl/model.py Outdated
before inputting to the next layer. None implies no
scaling is applied. A single value implies applying
the scaling each layer with the same value. For different
gammas per layer, pass an array like type.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a float is also an array-like in NumPy speak, so we should probably reword a bit

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

@tylerjereddy tylerjereddy left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I provided a more detailed review now that the diff is much cleaner (thanks).

I suppose the amount of refinement we want to apply here depends on the empirical proof of #71 (comment). At least I feel like I'm clearer on the criterion we have on the value of gamma scaling now.

Comment thread src/gfdl/tests/test_model.py Outdated


@pytest.mark.parametrize("gamma, weight_scheme, expected_acc, expected_roc", [
(None, "normal", 0.9472222222222222, 0.9889187266963562),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor: spacing vs. below

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

Comment thread src/gfdl/tests/test_model.py Outdated
@pytest.mark.parametrize("gamma, weight_scheme, expected_acc, expected_roc", [
(None, "normal", 0.9472222222222222, 0.9889187266963562),
(0.5, "normal", 0.9638888888888889, 0.9936661671672866),
([0.5, 0.5, 0.5, 0.5], "he_normal", 0.975, 0.9952345941275027),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor: maybe [0.5] * 4 ; [100] * 4 is done below for concision; hardly matters though

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.


@pytest.mark.parametrize("gamma, weight_scheme, expected_acc, expected_roc", [
(None, "normal", 0.9472222222222222, 0.9889187266963562),
(0.5, "normal", 0.9638888888888889, 0.9936661671672866),

@tylerjereddy tylerjereddy Apr 15, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion for a useful test case here is the same case with [0.5] * 4, just to show that the array and the scalar have the same effect in this case, when all gammas are equal across layers.

Sample suggestion from playing with this locally, that passes tests and includes a small comment:

--- a/src/gfdl/tests/test_model.py
+++ b/src/gfdl/tests/test_model.py
@@ -521,6 +521,8 @@ def test_rtol_ensemble(reg_alpha, rtol, expected_acc, expected_roc):
 @pytest.mark.parametrize("gamma, weight_scheme, expected_acc, expected_roc", [
      (None, "normal", 0.9472222222222222, 0.9889187266963562),
     (0.5, "normal", 0.9638888888888889, 0.9936661671672866),
+    # estimator performance unchanged with constant gamma array:
+    ([0.5] * 4 , "normal", 0.9638888888888889, 0.9936661671672866),
     ([0.5, 0.5, 0.5, 0.5], "he_normal", 0.975, 0.9952345941275027),
 ])
 def test_gamma_scaling_classifier(gamma, weight_scheme, expected_acc, expected_roc):

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good suggestion, added it.

roc_cur = roc_auc_score(y_test, y_hat_cur_proba, multi_class="ovo")

np.testing.assert_allclose(acc_cur, expected_acc)
np.testing.assert_allclose(roc_cur, expected_roc)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice test!

Of course you know I'm going to ask about other tests. It may depend on how far we go with gamma scaling, and how valuable it proves to be in the absence of gradient descent.

I'm thinking about the status of implementations and testing, and whether it even makes sense for:

  • EnsembleGFDLClassifier
  • GFDLRegressor

If they do make sense to include, we should probably open issues about that if we don't do it here (and I'm all for keeping the scope small initially, especially for the sanity of the reviewers).

Comment thread src/gfdl/tests/test_model.py Outdated


def test_gamma_scaling_size_mismatch_classifier():
# Test size mismatch exception between list of gamma's

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: gamma's -> gammas

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

Comment thread src/gfdl/model.py
scaling is applied. A single value implies applying
the scaling each layer with the same value. For different
gammas per layer, pass an array like type.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my suggestion to add a versionadded directive for new features in Emma's PR at: #47 (comment)

Of course, we don't yet know if we'll add this feature just yet.

Also, if we do add the feature, it would be good to add a reference that I think @shahyadk-bu was using for some of the work in the Notes section of the docstring. But only matters if we find this useful without gradients and actually want to add it into gfdl for real.

Comment thread src/gfdl/model.py Outdated
reg_alpha: float = None,
rtol: float = None
rtol: float = None,
gamma: float | np.typing.ArrayLike | None = None,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can fuse the float into array-like, since they're the same

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

Comment thread src/gfdl/model.py
reg_alpha=reg_alpha,
rtol=rtol)
rtol=rtol,
gamma=gamma)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you're going to hook these into GFDLRegressor here then it probably is fair game to ask GFDLRegressor to be tested with gamma as well.

Assuming we find that gamma is actually useful in the absence of gradients.

Comment thread src/gfdl/model.py
scaling is applied. A single value implies applying
the scaling each layer with the same value. For different
gammas per layer, pass an array like type.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

versionadded directive here as well, if we find that gamma is helpful in absence of gradients and should be included for GFDLRegressor

Comment thread src/gfdl/model.py Outdated
if np.isscalar(self.gamma):
H_prev *= 1.0 / (H_prev.shape[1] ** gamma)
else:
H_prev *= 1.0 / (H_prev.shape[1] ** self.gamma[i])

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the topic of input validation, do we really want to allow arbitrary floating point values of gamma without restriction? When I set gamma to -1000 in one of your tests I get a zero division error for obvious reasons.

Perhaps we should bound gamma a bit and raise ValueError when outside those bounds? Can we define sensible bounds? And then add a test for that of course.

(assuming we find gamma to be useful of course)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

@greptile-apps

greptile-apps Bot commented May 28, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds gamma scaling support to normalize layer outputs before feeding into the next layer, and refactors hidden-layer construction into two new methods (_init_weights, _construct_Hs) to reduce code duplication between fit() and predict().

  • gamma can be a scalar (applied uniformly to all layers) or a per-layer array; validation and range-checking ([0.5, 1.0]) are enforced in fit().
  • hidden_layer_sizes is now converted to a numpy array inside __init__ (previously done in fit()), and self.gamma is expanded from scalar to array during fit() — both patterns violate the sklearn BaseEstimator parameter contract and can break clone(), get_params(), and parametrize_with_checks.
  • New tests cover classifier accuracy, size-mismatch, and out-of-range gamma inputs, but GFDLRegressor has no corresponding gamma tests.

Confidence Score: 3/5

The core gamma scaling logic is sound, but two changes in model.py directly contradict the sklearn estimator contract in ways that affect cloning, serialization, and CV utilities.

Converting hidden_layer_sizes inside __init__ and overwriting self.gamma inside fit() both break BaseEstimator.get_params() / clone() reproducibility. These patterns are verified by the existing parametrize_with_checks suite, so they are likely to cause test failures rather than silent misbehavior, but they still represent real correctness issues that need to be resolved before merging.

src/gfdl/model.py needs attention for the two sklearn API violations; test_model.py is straightforward but should gain GFDLRegressor gamma coverage.

Important Files Changed

Filename Overview
src/gfdl/model.py Adds gamma scaling via _init_weights/_construct_Hs refactor; two sklearn API violations: hidden_layer_sizes converted in init and self.gamma mutated during fit().
src/gfdl/tests/test_model.py Adds classifier gamma tests (accuracy, size-mismatch, out-of-range); no equivalent tests for GFDLRegressor.

Sequence Diagram

sequenceDiagram
    participant User
    participant GFDLClassifier
    participant GFDL
    participant _init_weights
    participant _construct_Hs

    User->>GFDLClassifier: fit(X, y)
    GFDLClassifier->>GFDL: fit(X, Y_encoded)
    GFDL->>GFDL: "validate gamma & hidden_layer_sizes"
    Note over GFDL: scalar gamma expanded to array (mutates self.gamma)
    GFDL->>_init_weights: _init_weights(X)
    _init_weights-->>GFDL: self.W_, self.b_ populated
    GFDL->>_construct_Hs: _construct_Hs(X)
    loop each layer i
        _construct_Hs->>_construct_Hs: "Z = H_prev @ W[i].T + b[i]"
        _construct_Hs->>_construct_Hs: "H = activation(Z)"
        alt gamma is not None
            _construct_Hs->>_construct_Hs: "H *= 1 / (H.shape[1] ** gamma[i])"
        end
    end
    _construct_Hs-->>GFDL: Hs list
    GFDL->>GFDL: build D_ (design matrix)
    GFDL->>GFDL: solve for coeff_ (pinv or Ridge)
    GFDL-->>User: fitted estimator

    User->>GFDLClassifier: predict(X_test)
    GFDLClassifier->>GFDL: predict(X_test)
    GFDL->>_construct_Hs: _construct_Hs(X_test)
    Note over _construct_Hs: reuses same gamma scaling
    _construct_Hs-->>GFDL: Hs list
    GFDL-->>User: predictions
Loading

Reviews (1): Last reviewed commit: "MAINT: Fix lint errors" | Re-trigger Greptile

Comment thread src/gfdl/model.py
gamma: np.typing.ArrayLike | None = None,
):
self.hidden_layer_sizes = hidden_layer_sizes
self.hidden_layer_sizes = np.asarray(hidden_layer_sizes)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 sklearn parameter contract violated in __init__

BaseEstimator requires __init__ parameters to be stored verbatim (without transformation) so that get_params() and clone() return the original values. Calling np.asarray() here means a user who passes a Python list [100, 100] will get back array([100, 100]) from get_params(), and clone() will create a new estimator with a numpy array instead of the original list. This can break sklearn utilities like GridSearchCV and Pipeline, and will likely fail the existing parametrize_with_checks suite which verifies this property. The conversion belongs in _init_weights or fit(), as it was before this PR.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This LLM code review comment does seem valid. It is still undecided if we'll move forward with gamma scaling in the core library, but it would probably be useful if we could capture this requirement in our testsuite. Maybe an issue should be opened about that. It would be nice if we could use a testing utility from sklearn proper to enforce this, but it would also be straightforward to write our own test to do it.

Comment thread src/gfdl/model.py
Comment on lines +99 to +110
if self.gamma is not None:
if not np.isscalar(self.gamma):
if len(self.gamma) != len(self.hidden_layer_sizes):
raise ValueError("Mismatch between number of gamma values passed "
"and the total number of hidden layers. Expect them to be equal.")
else:
self.gamma = np.ones(len(self.hidden_layer_sizes)) * self.gamma

self.gamma = np.asarray(self.gamma, dtype=float)
if np.any((self.gamma > 1.0) | (self.gamma < 0.5)):
raise ValueError("Out of range gamma. Expected range : "
"None or [0.5, 1.0].")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 self.gamma permanently mutated during fit(), breaking sklearn conventions

Lines 105 and 107 overwrite self.gamma — a constructor parameter — with a derived value. After the first fit() call with gamma=0.5, self.gamma becomes np.array([0.5, 0.5, 0.5, 0.5]). Any subsequent clone() call on the fitted estimator (e.g. during cross-validation via clone(fitted_estimator)) will initialise the clone with the array version, silently discarding the user's original scalar intent. The standard sklearn pattern is to leave constructor parameters unchanged and store the processed value under a separate fitted attribute (e.g. self.gamma_), or perform the expansion at use-time inside _construct_Hs.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to above, it would be good if we could capture this kind of requirement in a regression test, even if we don't ultimately move forward with gamma scaling in core lib.

Comment on lines 529 to +601
with pytest.raises(ValueError, match="must be > 0"):
clf.fit(X, y)


@pytest.mark.parametrize("gamma, weight_scheme, expected_acc, expected_roc", [
(None, "normal", 0.9472222222222222, 0.9889187266963562),
(0.5, "normal", 0.9638888888888889, 0.9936661671672866),
# estimator performance unchanged with constant gamma array:
([0.5] * 4, "normal", 0.9638888888888889, 0.9936661671672866),
([0.5] * 4, "he_normal", 0.975, 0.9952345941275027),
])
def test_gamma_scaling_classifier(gamma, weight_scheme, expected_acc, expected_roc):
# Tests that gamma scaling does improve the accuracy score for the digits
# dataset.
data = load_digits()
X, y = data.data, data.target
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2,
random_state=0)

scaler = StandardScaler()
X_train_s = scaler.fit_transform(X_train)
X_test_s = scaler.transform(X_test)

model = GFDLClassifier(hidden_layer_sizes=[100] * 4,
activation="sigmoid",
weight_scheme=weight_scheme,
seed=0,
gamma=gamma)
model.fit(X_train_s, y_train)

y_hat_cur = model.predict(X_test_s)
y_hat_cur_proba = model.predict_proba(X_test_s)

acc_cur = accuracy_score(y_test, y_hat_cur)
roc_cur = roc_auc_score(y_test, y_hat_cur_proba, multi_class="ovo")

np.testing.assert_allclose(acc_cur, expected_acc)
np.testing.assert_allclose(roc_cur, expected_roc)


def test_gamma_scaling_size_mismatch_classifier():
# properly handle case where `gamma` parameter has size mismatch with
# hidden_layer_sizes
data = load_digits()
X, y = data.data, data.target
X_train, _, y_train, _ = train_test_split(X, y, test_size=0.2,
random_state=0)

X_train_s = StandardScaler().fit_transform(X_train)
model = GFDLClassifier(hidden_layer_sizes=[100] * 4,
activation="sigmoid",
weight_scheme="normal",
seed=0,
gamma=[0.5, 0.7])
with pytest.raises(ValueError, match="Mismatch between number of gamma values"):
model.fit(X_train_s, y_train)


def test_gamma_scaling_invalid_input_classifier():
# properly handle case where `gamma` parameter is out of range
data = load_digits()
X, y = data.data, data.target
X_train, _, y_train, _ = train_test_split(X, y, test_size=0.2,
random_state=0)

X_train_s = StandardScaler().fit_transform(X_train)
model = GFDLClassifier(hidden_layer_sizes=[100, 100],
activation="sigmoid",
weight_scheme="normal",
seed=0,
gamma=[-0.8, 100])
with pytest.raises(ValueError, match="Out of range"):
model.fit(X_train_s, y_train)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 No GFDLRegressor gamma tests

GFDLRegressor receives the same gamma parameter in this PR but there are no corresponding tests exercising it. The validation logic in GFDL.fit() is shared, but any regressor-specific code path (e.g. the way Y is shaped) combined with gamma scaling is untested. Consider adding at least a smoke-test and an equivalent size-mismatch/out-of-range test for GFDLRegressor.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

sdtemple pushed a commit that referenced this pull request Aug 12, 2026
* Related to gh-111, but only aims to address a subset
of it, and only for classifiers for now. If others want
to help deal with regressors and other parts of the issue
that is welcome.

* The regression test added here should now cause a failure
when the violation of `sklearn` `__init__` conventions
from #70 (comment)
is introduced into the source code.

* Although I did not use AI in the preparatio of this branch,
the original review comment above is from the greptile
AI reviewer.
sdtemple pushed a commit that referenced this pull request Sep 2, 2026
* Related to gh-111, but only aims to address a subset
of it. This is the regression equivalent to the classifier-based
testing added in gh-112.

* Similar to that other PR, the regression test added here also
causes a failure when introducing the API violation from
#70 (comment).

* I did not use AI in the prepartion of this PR, though note
that the original review comment that triggered all of this work
was from the greptile AI reviewer on GitHub in gh-70.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants