Repository: scikit-learn/scikit-learn
Pull request: FIX validate input array-like in KDTree and BallTree
#### Reference Issues/PRs
Closes #14650 
Superseded and closes #17400

Problem statement:
KDTree / BallTree seg fault
Both tree structures seg fault on init if the supplied data array has points with different dimensions.

```python
import numpy as np
from sklearn.neighbors import KDTree


X = [(1,2,3), (2,5), (5,5,1,2)]
Y = np.array(X)
KDTree(Y)
```
> Segmentation fault (core dumped)

Pip freeze:
> joblib==0.13.2
> numpy==1.17.0
> scikit-learn==0.21.3
> scipy==1.3.1
> sklearn==0.0

Hints from issue discussion:
I think that might rather be an array with an object dtype. Thanks for the
report

A PR with some validation of the passed `X` array would be welcome to fix this.

> I think that might rather be an array with an object dtype. 

I can confirm.
Would you be interested in proposing a PR to fix it @drejanko ? Arrays with non numeric dtype should raise an exception; possibly using `check_array` but that may have a negative performance impact.
Will do. 
Any update? Would like to submit a PR for this
Please do @mina1987 
Take

Commit message: Move validation before shape call.

Patch to review:
diff --git a/doc/whats_new/v0.24.rst b/doc/whats_new/v0.24.rst
index 21009245cc654..54b5806433ac7 100644
--- a/doc/whats_new/v0.24.rst
+++ b/doc/whats_new/v0.24.rst
@@ -583,6 +583,11 @@ Changelog
   the data intrinsic dimensionality is too high for tree-based methods.
   :pr:`17148` by :user:`Geoffrey Bolmier <gbolmier>`.
 
+- |Fix| :class:`neighbors.BinaryTree`
+  will raise a `ValueError` when fitting on data array having points with
+  different dimensions.
+  :pr:`18691` by :user:`Chiara Marmo <cmarmo>`.
+
 - |Fix| :class:`neighbors.NearestCentroid` with a numerical `shrink_threshold`
   will raise a `ValueError` when fitting on data with all constant features.
   :pr:`18370` by :user:`Trevor Waite <trewaite>`.
diff --git a/sklearn/neighbors/_binary_tree.pxi b/sklearn/neighbors/_binary_tree.pxi
index a632477c936fb..68f425da027f2 100755
--- a/sklearn/neighbors/_binary_tree.pxi
+++ b/sklearn/neighbors/_binary_tree.pxi
@@ -1055,10 +1055,11 @@ cdef class BinaryTree:
         if leaf_size < 1:
             raise ValueError("leaf_size must be greater than or equal to 1")
 
+        self.data_arr = check_array(data, dtype=DTYPE, order='C')
+
         n_samples = data.shape[0]
         n_features = data.shape[1]
 
-        self.data_arr = np.asarray(data, dtype=DTYPE, order='C')
         self.leaf_size = leaf_size
         self.dist_metric = DistanceMetric.get_metric(metric, **kwargs)
         self.euclidean = (self.dist_metric.__class__.__name__
diff --git a/sklearn/neighbors/tests/test_ball_tree.py b/sklearn/neighbors/tests/test_ball_tree.py
index 8da703dbe207d..353bf483a7cd5 100644
--- a/sklearn/neighbors/tests/test_ball_tree.py
+++ b/sklearn/neighbors/tests/test_ball_tree.py
@@ -65,3 +65,14 @@ def test_query_haversine():
 
     assert_array_almost_equal(dist1, dist2)
     assert_array_almost_equal(ind1, ind2)
+
+
+def test_array_object_type():
+    X = [(1, 2, 3), (2, 5), (5, 5, 1, 2)]
+    # dtype parameter enforced by NEP 34
+    Y = np.array(X, dtype=object)
+    with pytest.raises(
+        ValueError,
+        match="setting an array element with a sequence"
+    ):
+        BallTree(Y)


Code review comments:
diff_hunk: @@ -65,3 +65,14 @@ def test_query_haversine():
 
     assert_array_almost_equal(dist1, dist2)
     assert_array_almost_equal(ind1, ind2)
+
+
+def test_array_object_type():
+    X = [(1, 2, 3), (2, 5), (5, 5, 1, 2)]
+    # dtype parameter enforced by NEP 34
path: sklearn/neighbors/tests/test_ball_tree.py
text: @user1:
```suggestion
```

diff_hunk: @@ -1055,10 +1055,11 @@ cdef class BinaryTree:
         if leaf_size < 1:
             raise ValueError("leaf_size must be greater than or equal to 1")
 
+        self.data_arr = check_array(data, dtype=DTYPE, order='C')
path: sklearn/neighbors/_binary_tree.pxi
text: @user1:
We should move this validation on the top of `__init__` and change all `data.xxx` to `self.data_arr` to be sure that we use the validate array which will be a NumPy array.