Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion onedal/tests/utils/_dataframes_support.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
from onedal.tests.utils._device_selection import get_queues

test_frameworks = os.environ.get(
"ONEDAL_PYTEST_FRAMEWORKS", "numpy,pandas,dpnp,array_api"
"ONEDAL_PYTEST_FRAMEWORKS", "numpy,pandas,dpnp,array_api,torch"
)


Expand Down Expand Up @@ -126,6 +126,8 @@ def get_df_and_q(dataframe: str):
or array_api_enabled()
):
dataframes_and_queues.append(pytest.param("array_api", None, id="array_api"))
if torch_available and "torch" in dataframe_filter_:
dataframes_and_queues.extend(get_df_and_q("torch"))

return dataframes_and_queues

Expand All @@ -134,6 +136,8 @@ def _as_numpy(obj, *args, **kwargs):
"""Converted input object to numpy.ndarray format."""
if dpnp_available and isinstance(obj, dpnp.ndarray):
return obj.asnumpy(*args, **kwargs)
if torch_available and isinstance(obj, torch.Tensor):
return obj.cpu().detach().numpy(*args, **kwargs)
if isinstance(obj, pd.DataFrame) or isinstance(obj, pd.Series):
return obj.to_numpy(*args, **kwargs)
if sp.issparse(obj):
Expand Down Expand Up @@ -172,5 +176,18 @@ def _convert_to_dataframe(obj, sycl_queue=None, target_df=None, *args, **kwargs)

xp = array_api_modules[target_df]
return xp.asarray(obj)
elif target_df == "torch":
if "dtype" in kwargs:
kwargs["dtype"] = torch.from_numpy(np.empty(0, dtype=kwargs["dtype"])).dtype
# Mirror the requested sycl_queue's device so torch tensors don't land on
# xpu for CPU-queue cases (dpnp honors sycl_queue; torch must too).
is_gpu = sycl_queue is not None and getattr(
sycl_queue.sycl_device, "is_gpu", False
)
if is_gpu and hasattr(torch, "xpu") and torch.xpu.is_available():
device = "xpu"
else:
device = "cpu"
return torch.as_tensor(obj, device=device, *args, **kwargs)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Maybe this function should return an additional argument 'requires_array_api' or similar, and all the users should take care of activating array API when appropriate.


raise RuntimeError("Unsupported dataframe conversion")
22 changes: 20 additions & 2 deletions onedal/utils/_array_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import numpy as np
import scipy.sparse as sp

from ..utils._third_party import _is_subclass_fast
from ..utils._third_party import _is_subclass_fast, is_torch_tensor, lazy_import


def _supports_buffer_protocol(obj):
Expand Down Expand Up @@ -73,17 +73,35 @@ def _cls_to_sycl_namespace(cls):
raise ValueError(f"SYCL type not recognized: {cls}")


@lazy_import("array_api_compat")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

well written but unfortunately no longer necessary.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

how so?

def _torch_namespace(array_api_compat, array):
# torch's array API namespace is exposed via array_api_compat, not as a
# __array_namespace__ attribute on the tensor itself.
return array_api_compat.get_namespace(array)


def _is_sycl_array(x):
# dpnp exposes __sycl_usm_array_interface__; torch xpu tensors do not, so
# they are detected separately. Both must be recognized regardless of the
# array_api_dispatch global so compute-follows-data is preserved.
return hasattr(x, "__sycl_usm_array_interface__") or is_torch_tensor(x)


def _get_sycl_namespace(*arrays):
"""Get namespace of sycl arrays."""

# sycl support designed to work regardless of array_api_dispatch sklearn global value
sua_iface = {type(x): x for x in arrays if hasattr(x, "__sycl_usm_array_interface__")}
sua_iface = {type(x): x for x in arrays if _is_sycl_array(x)}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Perhaps it'd be easier to first remove support for DPNP without array API and then do these kinds of changes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

If you believe that would make most sense I could start looking into this

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Yes, would be better to first remove DPNP without array API, since it will allow removing lots of these functions.


if len(sua_iface) > 1:
raise ValueError(f"Multiple SYCL types for array inputs: {sua_iface}")

if sua_iface:
(X,) = sua_iface.values()
if is_torch_tensor(X):
# torch is array-API compliant via array_api_compat; report it as
# such so downstream conversions keep results as torch tensors.
return sua_iface, _torch_namespace(X), True
return (
sua_iface,
_cls_to_sycl_namespace(type(X)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
)
@pytest.mark.parametrize(
"dataframe,queue",
get_dataframes_and_queues(dataframe_filter_="dpnp", device_filter_="gpu"),
get_dataframes_and_queues(dataframe_filter_="dpnp,torch", device_filter_="gpu"),
)
@pytest.mark.mpi
def test_basic_stats_spmd_gold(dataframe, queue):
Expand Down Expand Up @@ -82,7 +82,7 @@ def test_basic_stats_spmd_gold(dataframe, queue):
@pytest.mark.parametrize("dtype", [np.float32, np.float64])
@pytest.mark.parametrize(
"dataframe,queue",
get_dataframes_and_queues(dataframe_filter_="dpnp", device_filter_="gpu"),
get_dataframes_and_queues(dataframe_filter_="dpnp,torch", device_filter_="gpu"),
)
@pytest.mark.parametrize("array_api_dispatch", [True, False])
@pytest.mark.mpi
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
)
@pytest.mark.parametrize(
"dataframe,queue",
get_dataframes_and_queues(dataframe_filter_="dpnp", device_filter_="gpu"),
get_dataframes_and_queues(dataframe_filter_="dpnp,torch", device_filter_="gpu"),
)
@pytest.mark.parametrize("weighted", [True, False])
@pytest.mark.parametrize("dtype", [np.float32, np.float64])
Expand Down Expand Up @@ -104,7 +104,7 @@ def test_incremental_basic_statistics_fit_spmd_gold(dataframe, queue, weighted,
)
@pytest.mark.parametrize(
"dataframe,queue",
get_dataframes_and_queues(dataframe_filter_="dpnp", device_filter_="gpu"),
get_dataframes_and_queues(dataframe_filter_="dpnp,torch", device_filter_="gpu"),
)
@pytest.mark.parametrize("num_blocks", [1, 2])
@pytest.mark.parametrize("weighted", [True, False])
Expand Down Expand Up @@ -178,7 +178,7 @@ def test_incremental_basic_statistics_partial_fit_spmd_gold(
)
@pytest.mark.parametrize(
"dataframe,queue",
get_dataframes_and_queues(dataframe_filter_="dpnp", device_filter_="gpu"),
get_dataframes_and_queues(dataframe_filter_="dpnp,torch", device_filter_="gpu"),
)
@pytest.mark.parametrize("num_blocks", [1, 2])
@pytest.mark.parametrize("weighted", [True, False])
Expand Down Expand Up @@ -247,7 +247,7 @@ def test_incremental_basic_statistics_single_option_partial_fit_spmd_gold(
)
@pytest.mark.parametrize(
"dataframe,queue",
get_dataframes_and_queues(dataframe_filter_="dpnp", device_filter_="gpu"),
get_dataframes_and_queues(dataframe_filter_="dpnp,torch", device_filter_="gpu"),
)
@pytest.mark.parametrize("num_blocks", [1, 2])
@pytest.mark.parametrize("weighted", [True, False])
Expand Down
4 changes: 2 additions & 2 deletions sklearnex/spmd/cluster/tests/test_dbscan_spmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
)
@pytest.mark.parametrize(
"dataframe,queue",
get_dataframes_and_queues(dataframe_filter_="dpnp", device_filter_="gpu"),
get_dataframes_and_queues(dataframe_filter_="dpnp,torch", device_filter_="gpu"),
)
@pytest.mark.mpi
def test_dbscan_spmd_gold(dataframe, queue):
Expand Down Expand Up @@ -67,7 +67,7 @@ def test_dbscan_spmd_gold(dataframe, queue):
@pytest.mark.parametrize("min_samples", [2, 5, 15])
@pytest.mark.parametrize(
"dataframe,queue",
get_dataframes_and_queues(dataframe_filter_="dpnp", device_filter_="gpu"),
get_dataframes_and_queues(dataframe_filter_="dpnp,torch", device_filter_="gpu"),
)
@pytest.mark.parametrize("dtype", [np.float32, np.float64])
@pytest.mark.parametrize("array_api_dispatch", [True, False])
Expand Down
4 changes: 2 additions & 2 deletions sklearnex/spmd/cluster/tests/test_kmeans_spmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
)
@pytest.mark.parametrize(
"dataframe,queue",
get_dataframes_and_queues(dataframe_filter_="dpnp", device_filter_="gpu"),
get_dataframes_and_queues(dataframe_filter_="dpnp,torch", device_filter_="gpu"),
)
@pytest.mark.mpi
def test_kmeans_spmd_gold(dataframe, queue):
Expand Down Expand Up @@ -107,7 +107,7 @@ def test_kmeans_spmd_gold(dataframe, queue):
@pytest.mark.parametrize("n_clusters", [2, 5, 15])
@pytest.mark.parametrize(
"dataframe,queue",
get_dataframes_and_queues(dataframe_filter_="dpnp", device_filter_="gpu"),
get_dataframes_and_queues(dataframe_filter_="dpnp,torch", device_filter_="gpu"),
)
@pytest.mark.parametrize("dtype", [np.float32, np.float64])
@pytest.mark.parametrize("array_api_dispatch", [True, False])
Expand Down
4 changes: 2 additions & 2 deletions sklearnex/spmd/covariance/tests/test_covariance_spmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
)
@pytest.mark.parametrize(
"dataframe,queue",
get_dataframes_and_queues(dataframe_filter_="dpnp", device_filter_="gpu"),
get_dataframes_and_queues(dataframe_filter_="dpnp,torch", device_filter_="gpu"),
)
@pytest.mark.mpi
def test_covariance_spmd_gold(dataframe, queue):
Expand Down Expand Up @@ -83,7 +83,7 @@ def test_covariance_spmd_gold(dataframe, queue):
@pytest.mark.parametrize("assume_centered", [True, False])
@pytest.mark.parametrize(
"dataframe,queue",
get_dataframes_and_queues(dataframe_filter_="dpnp", device_filter_="gpu"),
get_dataframes_and_queues(dataframe_filter_="dpnp,torch", device_filter_="gpu"),
)
@pytest.mark.parametrize("dtype", [np.float32, np.float64])
@pytest.mark.parametrize("array_api_dispatch", [True, False])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
)
@pytest.mark.parametrize(
"dataframe,queue",
get_dataframes_and_queues(dataframe_filter_="dpnp", device_filter_="gpu"),
get_dataframes_and_queues(dataframe_filter_="dpnp,torch", device_filter_="gpu"),
)
@pytest.mark.parametrize("assume_centered", [True, False])
@pytest.mark.parametrize("dtype", [np.float32, np.float64])
Expand Down Expand Up @@ -88,7 +88,7 @@ def test_incremental_covariance_fit_spmd_gold(dataframe, queue, assume_centered,
)
@pytest.mark.parametrize(
"dataframe,queue",
get_dataframes_and_queues(dataframe_filter_="dpnp", device_filter_="gpu"),
get_dataframes_and_queues(dataframe_filter_="dpnp,torch", device_filter_="gpu"),
)
@pytest.mark.parametrize("num_blocks", [1, 2])
@pytest.mark.parametrize("assume_centered", [True, False])
Expand Down Expand Up @@ -149,7 +149,7 @@ def test_incremental_covariance_partial_fit_spmd_gold(
@pytest.mark.parametrize("dtype", [np.float32, np.float64])
@pytest.mark.parametrize(
"dataframe,queue",
get_dataframes_and_queues(dataframe_filter_="dpnp", device_filter_="gpu"),
get_dataframes_and_queues(dataframe_filter_="dpnp,torch", device_filter_="gpu"),
)
@pytest.mark.parametrize("array_api_dispatch", [True, False])
@pytest.mark.mpi
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
)
@pytest.mark.parametrize(
"dataframe,queue",
get_dataframes_and_queues(dataframe_filter_="dpnp", device_filter_="gpu"),
get_dataframes_and_queues(dataframe_filter_="dpnp,torch", device_filter_="gpu"),
)
@pytest.mark.parametrize("whiten", [True, False])
@pytest.mark.parametrize("dtype", [np.float32, np.float64])
Expand Down Expand Up @@ -95,7 +95,7 @@ def test_incremental_pca_fit_spmd_gold(dataframe, queue, whiten, dtype):
)
@pytest.mark.parametrize(
"dataframe,queue",
get_dataframes_and_queues(dataframe_filter_="dpnp", device_filter_="gpu"),
get_dataframes_and_queues(dataframe_filter_="dpnp,torch", device_filter_="gpu"),
)
@pytest.mark.parametrize("whiten", [True, False])
@pytest.mark.parametrize("num_blocks", [1, 2])
Expand Down Expand Up @@ -159,7 +159,7 @@ def test_incremental_pca_partial_fit_spmd_gold(
)
@pytest.mark.parametrize(
"dataframe,queue",
get_dataframes_and_queues(dataframe_filter_="dpnp", device_filter_="gpu"),
get_dataframes_and_queues(dataframe_filter_="dpnp,torch", device_filter_="gpu"),
)
@pytest.mark.parametrize("whiten", [True, False])
@pytest.mark.parametrize("n_components", [None, 2, 5])
Expand Down Expand Up @@ -211,7 +211,7 @@ def test_incremental_pca_fit_spmd_random(
)
@pytest.mark.parametrize(
"dataframe,queue",
get_dataframes_and_queues(dataframe_filter_="dpnp", device_filter_="gpu"),
get_dataframes_and_queues(dataframe_filter_="dpnp,torch", device_filter_="gpu"),
)
@pytest.mark.parametrize("whiten", [True, False])
@pytest.mark.parametrize("n_components", [None, 2, 5])
Expand Down
4 changes: 2 additions & 2 deletions sklearnex/spmd/decomposition/tests/test_pca_spmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
)
@pytest.mark.parametrize(
"dataframe,queue",
get_dataframes_and_queues(dataframe_filter_="dpnp", device_filter_="gpu"),
get_dataframes_and_queues(dataframe_filter_="dpnp,torch", device_filter_="gpu"),
)
@pytest.mark.mpi
def test_pca_spmd_gold(dataframe, queue):
Expand Down Expand Up @@ -90,7 +90,7 @@ def test_pca_spmd_gold(dataframe, queue):
@pytest.mark.parametrize("whiten", [True, False])
@pytest.mark.parametrize(
"dataframe,queue",
get_dataframes_and_queues(dataframe_filter_="dpnp", device_filter_="gpu"),
get_dataframes_and_queues(dataframe_filter_="dpnp,torch", device_filter_="gpu"),
)
@pytest.mark.parametrize("dtype", [np.float32, np.float64])
@pytest.mark.parametrize("array_api_dispatch", [True, False])
Expand Down
8 changes: 4 additions & 4 deletions sklearnex/spmd/ensemble/tests/test_forest_spmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
)
@pytest.mark.parametrize(
"dataframe,queue",
get_dataframes_and_queues(dataframe_filter_="dpnp", device_filter_="gpu"),
get_dataframes_and_queues(dataframe_filter_="dpnp,torch", device_filter_="gpu"),
)
@pytest.mark.mpi
def test_rfcls_spmd_gold(dataframe, queue):
Expand Down Expand Up @@ -107,7 +107,7 @@ def test_rfcls_spmd_gold(dataframe, queue):
@pytest.mark.parametrize("local_trees_mode", [False, True])
@pytest.mark.parametrize(
"dataframe,queue",
get_dataframes_and_queues(dataframe_filter_="dpnp", device_filter_="gpu"),
get_dataframes_and_queues(dataframe_filter_="dpnp,torch", device_filter_="gpu"),
)
@pytest.mark.parametrize("dtype", [np.float32, np.float64])
@pytest.mark.parametrize("array_api_dispatch", [True, False])
Expand Down Expand Up @@ -173,7 +173,7 @@ def test_rfcls_spmd_synthetic(
)
@pytest.mark.parametrize(
"dataframe,queue",
get_dataframes_and_queues(dataframe_filter_="dpnp", device_filter_="gpu"),
get_dataframes_and_queues(dataframe_filter_="dpnp,torch", device_filter_="gpu"),
)
@pytest.mark.mpi
def test_rfreg_spmd_gold(dataframe, queue):
Expand Down Expand Up @@ -242,7 +242,7 @@ def test_rfreg_spmd_gold(dataframe, queue):
@pytest.mark.parametrize("local_trees_mode", [False, True])
@pytest.mark.parametrize(
"dataframe,queue",
get_dataframes_and_queues(dataframe_filter_="dpnp", device_filter_="gpu"),
get_dataframes_and_queues(dataframe_filter_="dpnp,torch", device_filter_="gpu"),
)
@pytest.mark.parametrize("dtype", [np.float32, np.float64])
@pytest.mark.parametrize("array_api_dispatch", [True, False])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
)
@pytest.mark.parametrize(
"dataframe,queue",
get_dataframes_and_queues(dataframe_filter_="dpnp", device_filter_="gpu"),
get_dataframes_and_queues(dataframe_filter_="dpnp,torch", device_filter_="gpu"),
)
@pytest.mark.parametrize("fit_intercept", [True, False])
@pytest.mark.parametrize("macro_block", [None, 1024])
Expand Down Expand Up @@ -112,7 +112,7 @@ def test_incremental_linear_regression_fit_spmd_gold(
)
@pytest.mark.parametrize(
"dataframe,queue",
get_dataframes_and_queues(dataframe_filter_="dpnp", device_filter_="gpu"),
get_dataframes_and_queues(dataframe_filter_="dpnp,torch", device_filter_="gpu"),
)
@pytest.mark.parametrize("fit_intercept", [True, False])
@pytest.mark.parametrize("num_blocks", [1, 2])
Expand Down Expand Up @@ -196,7 +196,7 @@ def test_incremental_linear_regression_partial_fit_spmd_gold(
)
@pytest.mark.parametrize(
"dataframe,queue",
get_dataframes_and_queues(dataframe_filter_="dpnp", device_filter_="gpu"),
get_dataframes_and_queues(dataframe_filter_="dpnp,torch", device_filter_="gpu"),
)
@pytest.mark.parametrize("fit_intercept", [True, False])
@pytest.mark.parametrize("num_samples", [100, 1000])
Expand Down Expand Up @@ -261,7 +261,7 @@ def test_incremental_linear_regression_fit_spmd_random(
)
@pytest.mark.parametrize(
"dataframe,queue",
get_dataframes_and_queues(dataframe_filter_="dpnp", device_filter_="gpu"),
get_dataframes_and_queues(dataframe_filter_="dpnp,torch", device_filter_="gpu"),
)
@pytest.mark.parametrize("fit_intercept", [True, False])
@pytest.mark.parametrize("num_blocks", [1, 2])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
)
@pytest.mark.parametrize(
"dataframe,queue",
get_dataframes_and_queues(dataframe_filter_="dpnp", device_filter_="gpu"),
get_dataframes_and_queues(dataframe_filter_="dpnp,torch", device_filter_="gpu"),
)
@pytest.mark.mpi
def test_linear_spmd_gold(dataframe, queue):
Expand Down Expand Up @@ -102,7 +102,7 @@ def test_linear_spmd_gold(dataframe, queue):
@pytest.mark.parametrize("n_features", [10, 100])
@pytest.mark.parametrize(
"dataframe,queue",
get_dataframes_and_queues(dataframe_filter_="dpnp", device_filter_="gpu"),
get_dataframes_and_queues(dataframe_filter_="dpnp,torch", device_filter_="gpu"),
)
@pytest.mark.parametrize("dtype", [np.float32, np.float64])
@pytest.mark.parametrize("array_api_dispatch", [True, False])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
)
@pytest.mark.parametrize(
"dataframe,queue",
get_dataframes_and_queues(dataframe_filter_="dpnp", device_filter_="gpu"),
get_dataframes_and_queues(dataframe_filter_="dpnp,torch", device_filter_="gpu"),
)
@pytest.mark.mpi
def test_logistic_spmd_gold(dataframe, queue):
Expand Down Expand Up @@ -112,7 +112,7 @@ def test_logistic_spmd_gold(dataframe, queue):
@pytest.mark.parametrize("tol", [1e-2, 1e-4])
@pytest.mark.parametrize(
"dataframe,queue",
get_dataframes_and_queues(dataframe_filter_="dpnp", device_filter_="gpu"),
get_dataframes_and_queues(dataframe_filter_="dpnp,torch", device_filter_="gpu"),
)
@pytest.mark.parametrize("dtype", [np.float32, np.float64])
@pytest.mark.parametrize("array_api_dispatch", [True, False])
Expand Down
Loading
Loading