2020import scipy .optimize as optimize
2121import scipy .sparse as sparse
2222import sklearn .linear_model ._logistic as logistic_module
23- from sklearn .linear_model ._sag import sag_solver
24- from sklearn .utils import (
25- check_array ,
26- check_consistent_length ,
27- check_random_state ,
28- compute_class_weight ,
23+ from sklearn .linear_model ._logistic import _LOGISTIC_SOLVER_CONVERGENCE_MSG
24+ from sklearn .linear_model ._logistic import (
25+ LogisticRegression as LogisticRegression_original ,
2926)
27+ from sklearn .linear_model ._logistic import _check_solver
28+ from sklearn .utils import check_array , check_consistent_length , check_random_state
3029from sklearn .utils .optimize import _check_optimize_result , _newton_cg
31- from sklearn .utils .validation import _check_sample_weight , check_is_fitted
30+ from sklearn .utils .validation import check_is_fitted
3231
3332import daal4py as d4p
3433
4443 _daal4py_loss_and_grad ,
4544)
4645
47- if sklearn_check_version ("1.1" ):
48- from sklearn ._loss .loss import HalfBinomialLoss , HalfMultinomialLoss
49- from sklearn .linear_model ._linear_loss import LinearModelLoss
50- from sklearn .linear_model ._logistic import _LOGISTIC_SOLVER_CONVERGENCE_MSG
51- from sklearn .linear_model ._logistic import (
52- LogisticRegression as LogisticRegression_original ,
53- )
54- from sklearn .linear_model ._logistic import (
55- _check_multi_class ,
56- _check_solver ,
57- _fit_liblinear ,
58- )
59- else :
60- from sklearn .linear_model ._logistic import _LOGISTIC_SOLVER_CONVERGENCE_MSG
61- from sklearn .linear_model ._logistic import (
62- LogisticRegression as LogisticRegression_original ,
63- )
64- from sklearn .linear_model ._logistic import (
65- _check_multi_class ,
66- _check_solver ,
67- _fit_liblinear ,
68- _logistic_grad_hess ,
69- _logistic_loss ,
70- _logistic_loss_and_grad ,
71- _multinomial_grad_hess ,
72- _multinomial_loss ,
73- _multinomial_loss_grad ,
74- )
75-
7646if sklearn_check_version ("1.7.1" ):
7747 from sklearn .utils .fixes import _get_additional_lbfgs_options_dict
7848else :
@@ -86,6 +56,25 @@ def _get_additional_lbfgs_options_dict(k, v):
8656from sklearn .preprocessing import LabelBinarizer , LabelEncoder
8757
8858
59+ # This code is a patch for sklearn 1.8, which is related to https://github.com/scikit-learn/scikit-learn/pull/32073
60+ # where the multi_class keyword is deprecated and this aspect is removed.
61+ def _check_multi_class (multi_class , solver , n_classes ):
62+ """Computes the multi class type, either "multinomial" or "ovr".
63+ For `n_classes` > 2 and a solver that supports it, returns "multinomial".
64+ For all other cases, in particular binary classification, return "ovr".
65+ """
66+ if multi_class == "auto" :
67+ if solver in ("liblinear" ,):
68+ multi_class = "ovr"
69+ elif n_classes > 2 :
70+ multi_class = "multinomial"
71+ else :
72+ multi_class = "ovr"
73+ if multi_class == "multinomial" and solver in ("liblinear" ,):
74+ raise ValueError ("Solver %s does not support a multinomial backend." % solver )
75+ return multi_class
76+
77+
8978# Code adapted from sklearn.linear_model.logistic version 0.21
9079def __logistic_regression_path (
9180 X ,
@@ -110,46 +99,6 @@ def __logistic_regression_path(
11099 l1_ratio = None ,
111100 n_threads = 1 ,
112101):
113- _patching_status = PatchingConditionsChain (
114- "sklearn.linear_model.LogisticRegression.fit"
115- )
116- _dal_ready = _patching_status .and_conditions (
117- [
118- (
119- solver in ["lbfgs" , "newton-cg" ],
120- f"'{ solver } ' solver is not supported. "
121- "Only 'lbfgs' and 'newton-cg' solvers are supported." ,
122- ),
123- (not sparse .issparse (X ), "X is sparse. Sparse input is not supported." ),
124- (sample_weight is None , "Sample weights are not supported." ),
125- (class_weight is None , "Class weights are not supported." ),
126- ]
127- )
128- if not _dal_ready :
129- _patching_status .write_log ()
130- return lr_path_original (
131- X ,
132- y ,
133- pos_class = pos_class ,
134- Cs = Cs ,
135- fit_intercept = fit_intercept ,
136- max_iter = max_iter ,
137- tol = tol ,
138- verbose = verbose ,
139- solver = solver ,
140- coef = coef ,
141- class_weight = class_weight ,
142- dual = dual ,
143- penalty = penalty ,
144- intercept_scaling = intercept_scaling ,
145- multi_class = multi_class ,
146- random_state = random_state ,
147- check_input = check_input ,
148- max_squared_sum = max_squared_sum ,
149- sample_weight = sample_weight ,
150- l1_ratio = l1_ratio ,
151- ** ({"n_threads" : n_threads } if sklearn_check_version ("1.1" ) else {}),
152- )
153102
154103 # Comment 2025-08-04: this file might have dead code paths from unsupported solvers.
155104 # It appears to have initially been a copy-paste of scikit-learn with a few additions
@@ -269,7 +218,6 @@ def __logistic_regression_path(
269218 func = _daal4py_loss_
270219 grad = _daal4py_grad_
271220 hess = _daal4py_grad_hess_
272- warm_start_sag = {"coef" : w0 .T }
273221 else :
274222 target = y_bin
275223 if solver == "lbfgs" :
@@ -280,7 +228,6 @@ def __logistic_regression_path(
280228 func = _daal4py_loss_
281229 grad = _daal4py_grad_
282230 hess = _daal4py_grad_hess_
283- warm_start_sag = {"coef" : np .expand_dims (w0 , axis = 1 )}
284231
285232 coefs = list ()
286233 n_iter = np .zeros (len (Cs ), dtype = np .int32 )
@@ -385,8 +332,6 @@ def _func_(x, *args):
385332 for i , ci in enumerate (coefs ):
386333 coefs [i ] = np .delete (ci , 0 , axis = - 1 )
387334
388- _patching_status .write_log ()
389-
390335 return np .array (coefs ), np .array (Cs ), n_iter
391336
392337
@@ -427,20 +372,21 @@ def daal4py_predict(self, X, resultsToEvaluate):
427372 f"sklearn.linear_model.LogisticRegression.{ _function_name } "
428373 )
429374 if _function_name != "predict" :
375+ multi_class = getattr (self , "multi_class" , "auto" )
430376 _patching_status .and_conditions (
431377 [
432378 (
433379 self .classes_ .size == 2
434- or logistic_module . _check_multi_class (
435- self . multi_class if self . multi_class != "deprecated" else "auto" ,
380+ or _check_multi_class (
381+ multi_class if multi_class != "deprecated" else "auto" ,
436382 self .solver ,
437383 self .classes_ .size ,
438384 )
439385 != "ovr" ,
440386 f"selected multiclass option is not supported for n_classes > 2." ,
441387 ),
442388 (
443- not (self .classes_ .size == 2 and self . multi_class == "multinomial" ),
389+ not (self .classes_ .size == 2 and multi_class == "multinomial" ),
444390 "multi_class='multinomial' not supported with binary data" ,
445391 ),
446392 ],
@@ -502,52 +448,35 @@ def daal4py_predict(self, X, resultsToEvaluate):
502448 return LogisticRegression_original .predict_log_proba (self , X )
503449
504450
505- def logistic_regression_path (
506- X ,
507- y ,
508- pos_class = None ,
509- Cs = 10 ,
510- fit_intercept = True ,
511- max_iter = 100 ,
512- tol = 1e-4 ,
513- verbose = 0 ,
514- solver = "lbfgs" ,
515- coef = None ,
516- class_weight = None ,
517- dual = False ,
518- penalty = "l2" ,
519- intercept_scaling = 1.0 ,
520- multi_class = "auto" ,
521- random_state = None ,
522- check_input = True ,
523- max_squared_sum = None ,
524- sample_weight = None ,
525- l1_ratio = None ,
526- n_threads = 1 ,
527- ):
528- return __logistic_regression_path (
529- X ,
530- y ,
531- pos_class = pos_class ,
532- Cs = Cs ,
533- fit_intercept = fit_intercept ,
534- max_iter = max_iter ,
535- tol = tol ,
536- verbose = verbose ,
537- solver = solver ,
538- coef = coef ,
539- class_weight = class_weight ,
540- dual = dual ,
541- penalty = penalty ,
542- intercept_scaling = intercept_scaling ,
543- multi_class = multi_class ,
544- random_state = random_state ,
545- check_input = check_input ,
546- max_squared_sum = max_squared_sum ,
547- sample_weight = sample_weight ,
548- l1_ratio = l1_ratio ,
549- n_threads = n_threads ,
451+ def logistic_regression_path (* args , ** kwargs ):
452+
453+ _patching_status = PatchingConditionsChain (
454+ "sklearn.linear_model.LogisticRegression.fit"
455+ )
456+ _dal_ready = _patching_status .and_conditions (
457+ [
458+ (
459+ kwargs ["solver" ] in ["lbfgs" , "newton-cg" ],
460+ f"'{ kwargs ['solver' ]} ' solver is not supported. "
461+ "Only 'lbfgs' and 'newton-cg' solvers are supported." ,
462+ ),
463+ (not sparse .issparse (args [0 ]), "X is sparse. Sparse input is not supported." ),
464+ (kwargs ["sample_weight" ] is None , "Sample weights are not supported." ),
465+ (kwargs ["class_weight" ] is None , "Class weights are not supported." ),
466+ ]
550467 )
468+ if not _dal_ready :
469+ _patching_status .write_log ()
470+ return lr_path_original (* args , ** kwargs )
471+
472+ if sklearn_check_version ("1.8" ):
473+ kwargs .pop ("classes" , None )
474+ res = __logistic_regression_path (* (args [:2 ]), ** kwargs )
475+ else :
476+ res = __logistic_regression_path (* args , ** kwargs )
477+
478+ _patching_status .write_log ()
479+ return res
551480
552481
553482@control_n_jobs (
0 commit comments