diff --git a/python/cuml/cuml/internals/validation.py b/python/cuml/cuml/internals/validation.py index 7740919f26..6fcd06670f 100644 --- a/python/cuml/cuml/internals/validation.py +++ b/python/cuml/cuml/internals/validation.py @@ -656,7 +656,14 @@ def check_array( if not isinstance(array_dtype, np.dtype) and array_dtype is not None: array_dtype = _as_numpy_dtype(array_dtype) elif not isinstance(array_dtype, np.dtype): - array_dtype = None + # Objects implementing the numpy array protocol may not expose a + # ``dtype`` attribute themselves. Normalize these before selecting + # from the supported dtypes so their represented dtype is preserved. + if hasattr(array, "__array__"): + array = np.asarray(array) + array_dtype = array.dtype + else: + array_dtype = None # Infer proper output dtype if array_dtype is not None: diff --git a/python/cuml/cuml_accel_tests/upstream/scikit-learn/xfail-list.yaml b/python/cuml/cuml_accel_tests/upstream/scikit-learn/xfail-list.yaml index 2660c77845..585cf963be 100644 --- a/python/cuml/cuml_accel_tests/upstream/scikit-learn/xfail-list.yaml +++ b/python/cuml/cuml_accel_tests/upstream/scikit-learn/xfail-list.yaml @@ -638,10 +638,7 @@ - "sklearn.preprocessing.tests.test_polynomial::test_csr_polynomial_expansion_index_overflow[csr_array-False-True-3-2344]" - "sklearn.svm.tests.test_svm::test_custom_kernel_not_array_input[SVC]" - "sklearn.svm.tests.test_svm::test_custom_kernel_not_array_input[SVR]" - - "sklearn.tests.test_common::test_estimators[LinearSVC()-check_classifier_data_not_an_array]" - - "sklearn.tests.test_common::test_estimators[LinearSVR()-check_regressor_data_not_an_array]" - - "sklearn.tests.test_common::test_estimators[SVC()-check_classifier_data_not_an_array]" - - "sklearn.tests.test_common::test_estimators[SVR()-check_regressor_data_not_an_array]" + - "sklearn.tests.test_common::test_estimators[RandomForestRegressor()-check_regressor_data_not_an_array]" - "sklearn.tests.test_multioutput::test_base_chain_fit_and_predict_with_sparse_data_and_cv[csr_array]" - "sklearn.tests.test_multioutput::test_classifier_chain_fit_and_predict_with_sparse_data[csr_array]" - reason: Test is flaky with cuml.accel on scikit-learn <1.9 @@ -820,7 +817,6 @@ tests: - "sklearn.tests.test_common::test_estimators[KNeighborsRegressor()-check_supervised_y_no_nan]" - "sklearn.tests.test_common::test_estimators[RandomForestClassifier()-check_classifiers_multilabel_output_format_decision_function]" - - "sklearn.tests.test_common::test_estimators[RandomForestRegressor()-check_regressor_data_not_an_array]" - reason: test_estimators checks fail marker: cuml_accel_test_estimators strict: false diff --git a/python/cuml/tests/test_sklearn_compatibility.py b/python/cuml/tests/test_sklearn_compatibility.py index 45e6249477..a6d20620e7 100644 --- a/python/cuml/tests/test_sklearn_compatibility.py +++ b/python/cuml/tests/test_sklearn_compatibility.py @@ -222,9 +222,8 @@ def _all_cuml_estimators(): }, RandomForestRegressor: { "check_regressor_data_not_an_array": ( - "cuml defaults to float32 for non-arrays (while sklearn defaults to " - "float64). Our float32 and float64 results differ _just enough_ that " - "this test fails on tolerances." + "Predictions from repeated fits on equivalent inputs can differ " + "beyond the check's tolerance" ), "check_sample_weight_equivalence_on_dense_data": ( "RandomForest uses quantile-binned splits, so sample weighting is " @@ -258,9 +257,7 @@ def _all_cuml_estimators(): }, UMAP: { "check_transformer_data_not_an_array": ( - "cuml defaults to float32 for non-arrays (while sklearn defaults to " - "float64). Our float32 and float64 results differ _just enough_ that " - "this test fails on tolerances." + "UMAP does not have consistent fit_transform and transform outputs" ), "check_methods_sample_order_invariance": "UMAP results depend on sample order", "check_transformer_general": "UMAP does not have consistent fit_transform and transform outputs", diff --git a/python/cuml/tests/test_validation.py b/python/cuml/tests/test_validation.py index 3f011b7300..d089e60edf 100644 --- a/python/cuml/tests/test_validation.py +++ b/python/cuml/tests/test_validation.py @@ -739,6 +739,21 @@ def test_check_array_dtype(array, mem_type): assert out.dtype == "float32" +@pytest.mark.parametrize("dtype", ["float32", "float64"]) +@pytest.mark.parametrize("mem_type", ["device", "host", None]) +def test_check_array_array_protocol_preserves_dtype(dtype, mem_type): + class ArrayLike: + def __init__(self, array): + self.array = array + + def __array__(self, dtype=None, copy=None): + return self.array + + array = ArrayLike(np.array([[1, 2, 3]], dtype=dtype)) + out = check_array(array, dtype=("float32", "float64"), mem_type=mem_type) + assert out.dtype == dtype + + @example(mem_type="device", dtype="int32", order="C", shape=(3, 4)) @example(mem_type="host", dtype="float32", order="F", shape=(3,)) @given(