From fd05db239b6190f470426b6a6b042e98de94605c Mon Sep 17 00:00:00 2001 From: SnigdhaSarkar16 Date: Mon, 20 Oct 2025 15:32:59 +0530 Subject: [PATCH 1/3] Add sklearnex interface for polynomial kernel (poly_kernel) Signed-off-by: SnigdhaSarkar16 --- sklearnex/svm/poly_kernel.py | 52 +++++++++++++++++++ .../svm/tests/test_poly_kernel_sklearnex.py | 31 +++++++++++ 2 files changed, 83 insertions(+) create mode 100644 sklearnex/svm/poly_kernel.py create mode 100644 sklearnex/svm/tests/test_poly_kernel_sklearnex.py diff --git a/sklearnex/svm/poly_kernel.py b/sklearnex/svm/poly_kernel.py new file mode 100644 index 0000000000..5cc56fbd90 --- /dev/null +++ b/sklearnex/svm/poly_kernel.py @@ -0,0 +1,52 @@ +# ============================================================================== +# Copyright contributors to the oneDAL project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== + +import numpy as np +from onedal.primitives import poly_kernel as onedal_poly_kernel + +def poly_kernel(X, Y=None, gamma=1.0, coef0=0.0, degree=3, queue=None): + """ + sklearnex interface for the polynomial kernel using oneDAL backend. + + K(x, y) = (gamma * + coef0) ** degree + for each pair of rows x in X and y in Y. + + Parameters + ---------- + X : array-like of shape (n_samples_X, n_features) + Input feature array. + + Y : array-like of shape (n_samples_Y, n_features), default=None + Optional second feature array. If None, Y = X. + + gamma : float, default=1.0 + Scaling factor for the inner product. + + coef0 : float, default=0.0 + Constant term added to scaled inner product. + + degree : int, default=3 + Degree of the polynomial kernel. + + queue : SyclQueue or None, default=None + Optional SYCL queue for device execution. + + Returns + ------- + kernel_matrix : ndarray of shape (n_samples_X, n_samples_Y) + Polynomial kernel Gram matrix. + """ + return onedal_poly_kernel(X, Y=Y, gamma=gamma, coef0=coef0, degree=degree, queue=queue) diff --git a/sklearnex/svm/tests/test_poly_kernel_sklearnex.py b/sklearnex/svm/tests/test_poly_kernel_sklearnex.py new file mode 100644 index 0000000000..63c6029158 --- /dev/null +++ b/sklearnex/svm/tests/test_poly_kernel_sklearnex.py @@ -0,0 +1,31 @@ +# ============================================================================== +# Copyright contributors to the oneDAL project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== + +import numpy as np +from sklearnex.svm.poly_kernel import poly_kernel as skl_poly_kernel +from onedal.primitives import poly_kernel as onedal_poly_kernel + +def test_poly_kernel_basic(): + X = np.array([[1, 2], [3, 4]]) + Y = np.array([[5, 6], [7, 8]]) + + # sklearnex interface result + K_skl = skl_poly_kernel(X, Y, gamma=0.5, coef0=1.0, degree=2) + # direct oneDAL result + K_onedal = onedal_poly_kernel(X, Y, gamma=0.5, coef0=1.0, degree=2) + + # check they are close + assert np.allclose(K_skl, K_onedal) From 6c3c435d6d248d3ee5e912eac9b524a77a763d73 Mon Sep 17 00:00:00 2001 From: SnigdhaSarkar16 Date: Mon, 20 Oct 2025 15:42:55 +0530 Subject: [PATCH 2/3] Add sklearnex interface for polynomial kernel Signed-off-by: SnigdhaSarkar16 --- sklearnex/svm/poly_kernel.py | 10 +++++--- .../svm/tests/test_poly_kernel_sklearnex.py | 24 ++++++++++++------- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/sklearnex/svm/poly_kernel.py b/sklearnex/svm/poly_kernel.py index 5cc56fbd90..ba1c6b39ed 100644 --- a/sklearnex/svm/poly_kernel.py +++ b/sklearnex/svm/poly_kernel.py @@ -7,7 +7,7 @@ # # http://www.apache.org/licenses/LICENSE-2.0 # -# Unless required by applicable law or agreed to in writing, software +# Unless required by applicable law or agreed to in writing, # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and @@ -15,11 +15,13 @@ # ============================================================================== import numpy as np + from onedal.primitives import poly_kernel as onedal_poly_kernel + def poly_kernel(X, Y=None, gamma=1.0, coef0=0.0, degree=3, queue=None): """ - sklearnex interface for the polynomial kernel using oneDAL backend. + Sklearnex interface for the polynomial kernel using oneDAL backend. K(x, y) = (gamma * + coef0) ** degree for each pair of rows x in X and y in Y. @@ -49,4 +51,6 @@ def poly_kernel(X, Y=None, gamma=1.0, coef0=0.0, degree=3, queue=None): kernel_matrix : ndarray of shape (n_samples_X, n_samples_Y) Polynomial kernel Gram matrix. """ - return onedal_poly_kernel(X, Y=Y, gamma=gamma, coef0=coef0, degree=degree, queue=queue) + return onedal_poly_kernel( + X, Y=Y, gamma=gamma, coef0=coef0, degree=degree, queue=queue + ) diff --git a/sklearnex/svm/tests/test_poly_kernel_sklearnex.py b/sklearnex/svm/tests/test_poly_kernel_sklearnex.py index 63c6029158..0eee77b4cf 100644 --- a/sklearnex/svm/tests/test_poly_kernel_sklearnex.py +++ b/sklearnex/svm/tests/test_poly_kernel_sklearnex.py @@ -7,7 +7,7 @@ # # http://www.apache.org/licenses/LICENSE-2.0 # -# Unless required by applicable law or agreed to in writing, software +# Unless required by applicable law or agreed to in writing, # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and @@ -15,17 +15,23 @@ # ============================================================================== import numpy as np -from sklearnex.svm.poly_kernel import poly_kernel as skl_poly_kernel -from onedal.primitives import poly_kernel as onedal_poly_kernel + +from sklearnex.svm.poly_kernel import poly_kernel + def test_poly_kernel_basic(): X = np.array([[1, 2], [3, 4]]) Y = np.array([[5, 6], [7, 8]]) - # sklearnex interface result - K_skl = skl_poly_kernel(X, Y, gamma=0.5, coef0=1.0, degree=2) - # direct oneDAL result - K_onedal = onedal_poly_kernel(X, Y, gamma=0.5, coef0=1.0, degree=2) + K = poly_kernel(X, Y, gamma=1.0, coef0=0.0, degree=2) + + # expected polynomial kernel manually + expected = (np.dot(X, Y.T)) ** 2 + assert np.allclose(K, expected), "Polynomial kernel computation is incorrect" - # check they are close - assert np.allclose(K_skl, K_onedal) + +def test_poly_kernel_default_Y(): + X = np.array([[1, 2], [3, 4]]) + K = poly_kernel(X) + expected = (np.dot(X, X.T)) ** 3 # default degree=3 + assert np.allclose(K, expected), "Polynomial kernel with Y=None is incorrect" From a7cfaf9bb5c6379f86c45fe33b5b4859900f0b34 Mon Sep 17 00:00:00 2001 From: SnigdhaSarkar16 Date: Mon, 20 Oct 2025 15:50:15 +0530 Subject: [PATCH 3/3] Fix license header in new files --- sklearnex/svm/poly_kernel.py | 44 +++++++++++++------ .../svm/tests/test_poly_kernel_sklearnex.py | 28 +++++++----- 2 files changed, 48 insertions(+), 24 deletions(-) diff --git a/sklearnex/svm/poly_kernel.py b/sklearnex/svm/poly_kernel.py index ba1c6b39ed..c9ab6b74a1 100644 --- a/sklearnex/svm/poly_kernel.py +++ b/sklearnex/svm/poly_kernel.py @@ -7,7 +7,7 @@ # # http://www.apache.org/licenses/LICENSE-2.0 # -# Unless required by applicable law or agreed to in writing, +# Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and @@ -15,16 +15,25 @@ # ============================================================================== import numpy as np +from scipy import sparse +from sklearn.metrics.pairwise import polynomial_kernel as sklearn_poly_kernel from onedal.primitives import poly_kernel as onedal_poly_kernel -def poly_kernel(X, Y=None, gamma=1.0, coef0=0.0, degree=3, queue=None): +def poly_kernel( + X, + Y=None, + degree=3, + gamma=None, + coef0=1, + queue=None, +): """ - Sklearnex interface for the polynomial kernel using oneDAL backend. + Compute the polynomial kernel using the oneDAL backend when possible. - K(x, y) = (gamma * + coef0) ** degree - for each pair of rows x in X and y in Y. + Falls back to scikit-learn's ``polynomial_kernel`` for unsupported cases + such as sparse inputs. Parameters ---------- @@ -32,25 +41,34 @@ def poly_kernel(X, Y=None, gamma=1.0, coef0=0.0, degree=3, queue=None): Input feature array. Y : array-like of shape (n_samples_Y, n_features), default=None - Optional second feature array. If None, Y = X. - - gamma : float, default=1.0 - Scaling factor for the inner product. - - coef0 : float, default=0.0 - Constant term added to scaled inner product. + Optional second feature array. If None, ``Y = X``. degree : int, default=3 Degree of the polynomial kernel. + gamma : float, default=None + Scaling factor for the inner product. If None, ``1 / n_features`` is used. + + coef0 : float, default=1 + Constant term added to the scaled inner product. + queue : SyclQueue or None, default=None Optional SYCL queue for device execution. Returns ------- kernel_matrix : ndarray of shape (n_samples_X, n_samples_Y) - Polynomial kernel Gram matrix. + Computed polynomial kernel Gram matrix. """ + # Fall back to sklearn if sparse input + if sparse.issparse(X) or (Y is not None and sparse.issparse(Y)): + return sklearn_poly_kernel(X, Y, degree=degree, gamma=gamma, coef0=coef0) + + # Handle gamma default like sklearn + if gamma is None: + gamma = 1.0 / X.shape[1] + + # Use oneDAL accelerated path return onedal_poly_kernel( X, Y=Y, gamma=gamma, coef0=coef0, degree=degree, queue=queue ) diff --git a/sklearnex/svm/tests/test_poly_kernel_sklearnex.py b/sklearnex/svm/tests/test_poly_kernel_sklearnex.py index 0eee77b4cf..de272f35af 100644 --- a/sklearnex/svm/tests/test_poly_kernel_sklearnex.py +++ b/sklearnex/svm/tests/test_poly_kernel_sklearnex.py @@ -7,7 +7,7 @@ # # http://www.apache.org/licenses/LICENSE-2.0 # -# Unless required by applicable law or agreed to in writing, +# Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and @@ -15,23 +15,29 @@ # ============================================================================== import numpy as np +from scipy import sparse +from sklearn.metrics.pairwise import polynomial_kernel as sklearn_poly_kernel from sklearnex.svm.poly_kernel import poly_kernel -def test_poly_kernel_basic(): +def test_poly_kernel_dense(): + """Test poly_kernel on dense input arrays.""" X = np.array([[1, 2], [3, 4]]) Y = np.array([[5, 6], [7, 8]]) - K = poly_kernel(X, Y, gamma=1.0, coef0=0.0, degree=2) + result = poly_kernel(X, Y, degree=2, gamma=0.5, coef0=1) + expected = sklearn_poly_kernel(X, Y, degree=2, gamma=0.5, coef0=1) - # expected polynomial kernel manually - expected = (np.dot(X, Y.T)) ** 2 - assert np.allclose(K, expected), "Polynomial kernel computation is incorrect" + np.testing.assert_allclose(result, expected, rtol=1e-5, atol=1e-8) -def test_poly_kernel_default_Y(): - X = np.array([[1, 2], [3, 4]]) - K = poly_kernel(X) - expected = (np.dot(X, X.T)) ** 3 # default degree=3 - assert np.allclose(K, expected), "Polynomial kernel with Y=None is incorrect" +def test_poly_kernel_sparse_fallback(): + """Test that poly_kernel falls back to sklearn implementation for sparse inputs.""" + X = sparse.csr_matrix([[1, 0], [0, 1]]) + Y = sparse.csr_matrix([[0, 1], [1, 0]]) + + result = poly_kernel(X, Y, degree=3, gamma=1.0, coef0=1.0) + expected = sklearn_poly_kernel(X, Y, degree=3, gamma=1.0, coef0=1.0) + + np.testing.assert_allclose(result, expected, rtol=1e-5, atol=1e-8)