release(camera-ai): publish 0.4.12 catalog portability fix

This commit is contained in:
KINDNICK
2026-08-16 01:05:07 +09:00
parent 8e5e2e8ad0
commit b65e54a7e8
211 changed files with 45990 additions and 45913 deletions
@@ -1,284 +1,284 @@
{{py:
# Generated file: _ball_tree.pyx
implementation_specific_values = [
# The values are arranged as follows:
#
# name_suffix, INPUT_DTYPE_t, INPUT_DTYPE
#
('64', 'float64_t', 'np.float64'),
('32', 'float32_t', 'np.float32')
]
# Authors: The scikit-learn developers
# SPDX-License-Identifier: BSD-3-Clause
}}
__all__ = ['BallTree', 'BallTree64', 'BallTree32']
{{for name_suffix, INPUT_DTYPE_t, INPUT_DTYPE in implementation_specific_values}}
DOC_DICT{{name_suffix}} = {
'BinaryTree': 'BallTree{{name_suffix}}',
'binary_tree': 'ball_tree{{name_suffix}}',
}
VALID_METRICS{{name_suffix}} = [
'BrayCurtisDistance{{name_suffix}}',
'CanberraDistance{{name_suffix}}',
'ChebyshevDistance{{name_suffix}}',
'DiceDistance{{name_suffix}}',
'EuclideanDistance{{name_suffix}}',
'HammingDistance{{name_suffix}}',
'HaversineDistance{{name_suffix}}',
'JaccardDistance{{name_suffix}}',
'MahalanobisDistance{{name_suffix}}',
'ManhattanDistance{{name_suffix}}',
'MinkowskiDistance{{name_suffix}}',
'PyFuncDistance{{name_suffix}}',
'RogersTanimotoDistance{{name_suffix}}',
'RussellRaoDistance{{name_suffix}}',
'SEuclideanDistance{{name_suffix}}',
'SokalMichenerDistance{{name_suffix}}',
'SokalSneathDistance{{name_suffix}}',
'WMinkowskiDistance{{name_suffix}}',
]
{{endfor}}
include "_binary_tree.pxi"
{{for name_suffix, INPUT_DTYPE_t, INPUT_DTYPE in implementation_specific_values}}
# Inherit BallTree{{name_suffix}} from BinaryTree{{name_suffix}}
cdef class BallTree{{name_suffix}}(BinaryTree{{name_suffix}}):
__doc__ = CLASS_DOC.format(**DOC_DICT{{name_suffix}})
pass
{{endfor}}
#----------------------------------------------------------------------
# The functions below specialized the Binary Tree as a Ball Tree
#
# Note that these functions use the concept of "reduced distance".
# The reduced distance, defined for some metrics, is a quantity which
# is more efficient to compute than the distance, but preserves the
# relative rankings of the true distance. For example, the reduced
# distance for the Euclidean metric is the squared-euclidean distance.
# For some metrics, the reduced distance is simply the distance.
{{for name_suffix, INPUT_DTYPE_t, INPUT_DTYPE in implementation_specific_values}}
cdef int allocate_data{{name_suffix}}(
BinaryTree{{name_suffix}} tree,
intp_t n_nodes,
intp_t n_features,
) except -1:
"""Allocate arrays needed for the KD Tree"""
tree.node_bounds = np.zeros((1, n_nodes, n_features), dtype={{INPUT_DTYPE}})
return 0
cdef int init_node{{name_suffix}}(
BinaryTree{{name_suffix}} tree,
NodeData_t[::1] node_data,
intp_t i_node,
intp_t idx_start,
intp_t idx_end,
) except -1:
"""Initialize the node for the dataset stored in tree.data"""
cdef intp_t n_features = tree.data.shape[1]
cdef intp_t n_points = idx_end - idx_start
cdef intp_t i, j
cdef float64_t radius
cdef const {{INPUT_DTYPE_t}} *this_pt
cdef intp_t* idx_array = &tree.idx_array[0]
cdef const {{INPUT_DTYPE_t}}* data = &tree.data[0, 0]
cdef {{INPUT_DTYPE_t}}* centroid = &tree.node_bounds[0, i_node, 0]
cdef bint with_sample_weight = tree.sample_weight is not None
cdef const {{INPUT_DTYPE_t}}* sample_weight
cdef float64_t sum_weight_node
if with_sample_weight:
sample_weight = &tree.sample_weight[0]
# determine Node centroid
for j in range(n_features):
centroid[j] = 0
if with_sample_weight:
sum_weight_node = 0
for i in range(idx_start, idx_end):
sum_weight_node += sample_weight[idx_array[i]]
this_pt = data + n_features * idx_array[i]
for j from 0 <= j < n_features:
centroid[j] += this_pt[j] * sample_weight[idx_array[i]]
for j in range(n_features):
centroid[j] /= sum_weight_node
else:
for i in range(idx_start, idx_end):
this_pt = data + n_features * idx_array[i]
for j from 0 <= j < n_features:
centroid[j] += this_pt[j]
for j in range(n_features):
centroid[j] /= n_points
# determine Node radius
radius = 0
for i in range(idx_start, idx_end):
radius = fmax(radius,
tree.rdist(centroid,
data + n_features * idx_array[i],
n_features))
node_data[i_node].radius = tree.dist_metric._rdist_to_dist(radius)
node_data[i_node].idx_start = idx_start
node_data[i_node].idx_end = idx_end
return 0
cdef inline float64_t min_dist{{name_suffix}}(
BinaryTree{{name_suffix}} tree,
intp_t i_node,
const {{INPUT_DTYPE_t}}* pt,
) except -1 nogil:
"""Compute the minimum distance between a point and a node"""
cdef float64_t dist_pt = tree.dist(pt, &tree.node_bounds[0, i_node, 0],
tree.data.shape[1])
return fmax(0, dist_pt - tree.node_data[i_node].radius)
cdef inline float64_t max_dist{{name_suffix}}(
BinaryTree{{name_suffix}} tree,
intp_t i_node,
const {{INPUT_DTYPE_t}}* pt,
) except -1:
"""Compute the maximum distance between a point and a node"""
cdef float64_t dist_pt = tree.dist(pt, &tree.node_bounds[0, i_node, 0],
tree.data.shape[1])
return dist_pt + tree.node_data[i_node].radius
cdef inline int min_max_dist{{name_suffix}}(
BinaryTree{{name_suffix}} tree,
intp_t i_node,
const {{INPUT_DTYPE_t}}* pt,
float64_t* min_dist,
float64_t* max_dist,
) except -1 nogil:
"""Compute the minimum and maximum distance between a point and a node"""
cdef float64_t dist_pt = tree.dist(pt, &tree.node_bounds[0, i_node, 0],
tree.data.shape[1])
cdef float64_t rad = tree.node_data[i_node].radius
min_dist[0] = fmax(0, dist_pt - rad)
max_dist[0] = dist_pt + rad
return 0
cdef inline float64_t min_rdist{{name_suffix}}(
BinaryTree{{name_suffix}} tree,
intp_t i_node,
const {{INPUT_DTYPE_t}}* pt,
) except -1 nogil:
"""Compute the minimum reduced-distance between a point and a node"""
if tree.euclidean:
return euclidean_dist_to_rdist{{name_suffix}}(
min_dist{{name_suffix}}(tree, i_node, pt)
)
else:
return tree.dist_metric._dist_to_rdist(
min_dist{{name_suffix}}(tree, i_node, pt)
)
cdef inline float64_t max_rdist{{name_suffix}}(
BinaryTree{{name_suffix}} tree,
intp_t i_node,
const {{INPUT_DTYPE_t}}* pt,
) except -1:
"""Compute the maximum reduced-distance between a point and a node"""
if tree.euclidean:
return euclidean_dist_to_rdist{{name_suffix}}(
max_dist{{name_suffix}}(tree, i_node, pt)
)
else:
return tree.dist_metric._dist_to_rdist(
max_dist{{name_suffix}}(tree, i_node, pt)
)
cdef inline float64_t min_dist_dual{{name_suffix}}(
BinaryTree{{name_suffix}} tree1,
intp_t i_node1,
BinaryTree{{name_suffix}} tree2,
intp_t i_node2,
) except -1:
"""compute the minimum distance between two nodes"""
cdef float64_t dist_pt = tree1.dist(&tree2.node_bounds[0, i_node2, 0],
&tree1.node_bounds[0, i_node1, 0],
tree1.data.shape[1])
return fmax(0, (dist_pt - tree1.node_data[i_node1].radius
- tree2.node_data[i_node2].radius))
cdef inline float64_t max_dist_dual{{name_suffix}}(
BinaryTree{{name_suffix}} tree1,
intp_t i_node1,
BinaryTree{{name_suffix}} tree2,
intp_t i_node2,
) except -1:
"""compute the maximum distance between two nodes"""
cdef float64_t dist_pt = tree1.dist(&tree2.node_bounds[0, i_node2, 0],
&tree1.node_bounds[0, i_node1, 0],
tree1.data.shape[1])
return (dist_pt + tree1.node_data[i_node1].radius
+ tree2.node_data[i_node2].radius)
cdef inline float64_t min_rdist_dual{{name_suffix}}(
BinaryTree{{name_suffix}} tree1,
intp_t i_node1,
BinaryTree{{name_suffix}} tree2,
intp_t i_node2,
) except -1:
"""compute the minimum reduced distance between two nodes"""
if tree1.euclidean:
return euclidean_dist_to_rdist{{name_suffix}}(
min_dist_dual{{name_suffix}}(tree1, i_node1, tree2, i_node2)
)
else:
return tree1.dist_metric._dist_to_rdist(
min_dist_dual{{name_suffix}}(tree1, i_node1, tree2, i_node2)
)
cdef inline float64_t max_rdist_dual{{name_suffix}}(
BinaryTree{{name_suffix}} tree1,
intp_t i_node1,
BinaryTree{{name_suffix}} tree2,
intp_t i_node2,
) except -1:
"""compute the maximum reduced distance between two nodes"""
if tree1.euclidean:
return euclidean_dist_to_rdist{{name_suffix}}(
max_dist_dual{{name_suffix}}(tree1, i_node1, tree2, i_node2)
)
else:
return tree1.dist_metric._dist_to_rdist(
max_dist_dual{{name_suffix}}(tree1, i_node1, tree2, i_node2)
)
{{endfor}}
class BallTree(BallTree64):
__doc__ = CLASS_DOC.format(BinaryTree="BallTree")
pass
{{py:
# Generated file: _ball_tree.pyx
implementation_specific_values = [
# The values are arranged as follows:
#
# name_suffix, INPUT_DTYPE_t, INPUT_DTYPE
#
('64', 'float64_t', 'np.float64'),
('32', 'float32_t', 'np.float32')
]
# Authors: The scikit-learn developers
# SPDX-License-Identifier: BSD-3-Clause
}}
__all__ = ['BallTree', 'BallTree64', 'BallTree32']
{{for name_suffix, INPUT_DTYPE_t, INPUT_DTYPE in implementation_specific_values}}
DOC_DICT{{name_suffix}} = {
'BinaryTree': 'BallTree{{name_suffix}}',
'binary_tree': 'ball_tree{{name_suffix}}',
}
VALID_METRICS{{name_suffix}} = [
'BrayCurtisDistance{{name_suffix}}',
'CanberraDistance{{name_suffix}}',
'ChebyshevDistance{{name_suffix}}',
'DiceDistance{{name_suffix}}',
'EuclideanDistance{{name_suffix}}',
'HammingDistance{{name_suffix}}',
'HaversineDistance{{name_suffix}}',
'JaccardDistance{{name_suffix}}',
'MahalanobisDistance{{name_suffix}}',
'ManhattanDistance{{name_suffix}}',
'MinkowskiDistance{{name_suffix}}',
'PyFuncDistance{{name_suffix}}',
'RogersTanimotoDistance{{name_suffix}}',
'RussellRaoDistance{{name_suffix}}',
'SEuclideanDistance{{name_suffix}}',
'SokalMichenerDistance{{name_suffix}}',
'SokalSneathDistance{{name_suffix}}',
'WMinkowskiDistance{{name_suffix}}',
]
{{endfor}}
include "_binary_tree.pxi"
{{for name_suffix, INPUT_DTYPE_t, INPUT_DTYPE in implementation_specific_values}}
# Inherit BallTree{{name_suffix}} from BinaryTree{{name_suffix}}
cdef class BallTree{{name_suffix}}(BinaryTree{{name_suffix}}):
__doc__ = CLASS_DOC.format(**DOC_DICT{{name_suffix}})
pass
{{endfor}}
#----------------------------------------------------------------------
# The functions below specialized the Binary Tree as a Ball Tree
#
# Note that these functions use the concept of "reduced distance".
# The reduced distance, defined for some metrics, is a quantity which
# is more efficient to compute than the distance, but preserves the
# relative rankings of the true distance. For example, the reduced
# distance for the Euclidean metric is the squared-euclidean distance.
# For some metrics, the reduced distance is simply the distance.
{{for name_suffix, INPUT_DTYPE_t, INPUT_DTYPE in implementation_specific_values}}
cdef int allocate_data{{name_suffix}}(
BinaryTree{{name_suffix}} tree,
intp_t n_nodes,
intp_t n_features,
) except -1:
"""Allocate arrays needed for the KD Tree"""
tree.node_bounds = np.zeros((1, n_nodes, n_features), dtype={{INPUT_DTYPE}})
return 0
cdef int init_node{{name_suffix}}(
BinaryTree{{name_suffix}} tree,
NodeData_t[::1] node_data,
intp_t i_node,
intp_t idx_start,
intp_t idx_end,
) except -1:
"""Initialize the node for the dataset stored in tree.data"""
cdef intp_t n_features = tree.data.shape[1]
cdef intp_t n_points = idx_end - idx_start
cdef intp_t i, j
cdef float64_t radius
cdef const {{INPUT_DTYPE_t}} *this_pt
cdef intp_t* idx_array = &tree.idx_array[0]
cdef const {{INPUT_DTYPE_t}}* data = &tree.data[0, 0]
cdef {{INPUT_DTYPE_t}}* centroid = &tree.node_bounds[0, i_node, 0]
cdef bint with_sample_weight = tree.sample_weight is not None
cdef const {{INPUT_DTYPE_t}}* sample_weight
cdef float64_t sum_weight_node
if with_sample_weight:
sample_weight = &tree.sample_weight[0]
# determine Node centroid
for j in range(n_features):
centroid[j] = 0
if with_sample_weight:
sum_weight_node = 0
for i in range(idx_start, idx_end):
sum_weight_node += sample_weight[idx_array[i]]
this_pt = data + n_features * idx_array[i]
for j from 0 <= j < n_features:
centroid[j] += this_pt[j] * sample_weight[idx_array[i]]
for j in range(n_features):
centroid[j] /= sum_weight_node
else:
for i in range(idx_start, idx_end):
this_pt = data + n_features * idx_array[i]
for j from 0 <= j < n_features:
centroid[j] += this_pt[j]
for j in range(n_features):
centroid[j] /= n_points
# determine Node radius
radius = 0
for i in range(idx_start, idx_end):
radius = fmax(radius,
tree.rdist(centroid,
data + n_features * idx_array[i],
n_features))
node_data[i_node].radius = tree.dist_metric._rdist_to_dist(radius)
node_data[i_node].idx_start = idx_start
node_data[i_node].idx_end = idx_end
return 0
cdef inline float64_t min_dist{{name_suffix}}(
BinaryTree{{name_suffix}} tree,
intp_t i_node,
const {{INPUT_DTYPE_t}}* pt,
) except -1 nogil:
"""Compute the minimum distance between a point and a node"""
cdef float64_t dist_pt = tree.dist(pt, &tree.node_bounds[0, i_node, 0],
tree.data.shape[1])
return fmax(0, dist_pt - tree.node_data[i_node].radius)
cdef inline float64_t max_dist{{name_suffix}}(
BinaryTree{{name_suffix}} tree,
intp_t i_node,
const {{INPUT_DTYPE_t}}* pt,
) except -1:
"""Compute the maximum distance between a point and a node"""
cdef float64_t dist_pt = tree.dist(pt, &tree.node_bounds[0, i_node, 0],
tree.data.shape[1])
return dist_pt + tree.node_data[i_node].radius
cdef inline int min_max_dist{{name_suffix}}(
BinaryTree{{name_suffix}} tree,
intp_t i_node,
const {{INPUT_DTYPE_t}}* pt,
float64_t* min_dist,
float64_t* max_dist,
) except -1 nogil:
"""Compute the minimum and maximum distance between a point and a node"""
cdef float64_t dist_pt = tree.dist(pt, &tree.node_bounds[0, i_node, 0],
tree.data.shape[1])
cdef float64_t rad = tree.node_data[i_node].radius
min_dist[0] = fmax(0, dist_pt - rad)
max_dist[0] = dist_pt + rad
return 0
cdef inline float64_t min_rdist{{name_suffix}}(
BinaryTree{{name_suffix}} tree,
intp_t i_node,
const {{INPUT_DTYPE_t}}* pt,
) except -1 nogil:
"""Compute the minimum reduced-distance between a point and a node"""
if tree.euclidean:
return euclidean_dist_to_rdist{{name_suffix}}(
min_dist{{name_suffix}}(tree, i_node, pt)
)
else:
return tree.dist_metric._dist_to_rdist(
min_dist{{name_suffix}}(tree, i_node, pt)
)
cdef inline float64_t max_rdist{{name_suffix}}(
BinaryTree{{name_suffix}} tree,
intp_t i_node,
const {{INPUT_DTYPE_t}}* pt,
) except -1:
"""Compute the maximum reduced-distance between a point and a node"""
if tree.euclidean:
return euclidean_dist_to_rdist{{name_suffix}}(
max_dist{{name_suffix}}(tree, i_node, pt)
)
else:
return tree.dist_metric._dist_to_rdist(
max_dist{{name_suffix}}(tree, i_node, pt)
)
cdef inline float64_t min_dist_dual{{name_suffix}}(
BinaryTree{{name_suffix}} tree1,
intp_t i_node1,
BinaryTree{{name_suffix}} tree2,
intp_t i_node2,
) except -1:
"""compute the minimum distance between two nodes"""
cdef float64_t dist_pt = tree1.dist(&tree2.node_bounds[0, i_node2, 0],
&tree1.node_bounds[0, i_node1, 0],
tree1.data.shape[1])
return fmax(0, (dist_pt - tree1.node_data[i_node1].radius
- tree2.node_data[i_node2].radius))
cdef inline float64_t max_dist_dual{{name_suffix}}(
BinaryTree{{name_suffix}} tree1,
intp_t i_node1,
BinaryTree{{name_suffix}} tree2,
intp_t i_node2,
) except -1:
"""compute the maximum distance between two nodes"""
cdef float64_t dist_pt = tree1.dist(&tree2.node_bounds[0, i_node2, 0],
&tree1.node_bounds[0, i_node1, 0],
tree1.data.shape[1])
return (dist_pt + tree1.node_data[i_node1].radius
+ tree2.node_data[i_node2].radius)
cdef inline float64_t min_rdist_dual{{name_suffix}}(
BinaryTree{{name_suffix}} tree1,
intp_t i_node1,
BinaryTree{{name_suffix}} tree2,
intp_t i_node2,
) except -1:
"""compute the minimum reduced distance between two nodes"""
if tree1.euclidean:
return euclidean_dist_to_rdist{{name_suffix}}(
min_dist_dual{{name_suffix}}(tree1, i_node1, tree2, i_node2)
)
else:
return tree1.dist_metric._dist_to_rdist(
min_dist_dual{{name_suffix}}(tree1, i_node1, tree2, i_node2)
)
cdef inline float64_t max_rdist_dual{{name_suffix}}(
BinaryTree{{name_suffix}} tree1,
intp_t i_node1,
BinaryTree{{name_suffix}} tree2,
intp_t i_node2,
) except -1:
"""compute the maximum reduced distance between two nodes"""
if tree1.euclidean:
return euclidean_dist_to_rdist{{name_suffix}}(
max_dist_dual{{name_suffix}}(tree1, i_node1, tree2, i_node2)
)
else:
return tree1.dist_metric._dist_to_rdist(
max_dist_dual{{name_suffix}}(tree1, i_node1, tree2, i_node2)
)
{{endfor}}
class BallTree(BallTree64):
__doc__ = CLASS_DOC.format(BinaryTree="BallTree")
pass
File diff suppressed because it is too large Load Diff
@@ -1,336 +1,336 @@
{{py:
# Generated file: _kd_tree.pyx
implementation_specific_values = [
# The values are arranged as follows:
#
# name_suffix, INPUT_DTYPE_t, INPUT_DTYPE
#
('64', 'float64_t', 'np.float64'),
('32', 'float32_t', 'np.float32')
]
# By Jake Vanderplas (2013) <jakevdp@cs.washington.edu>
# written for the scikit-learn project
# SPDX-License-Identifier: BSD-3-Clause
}}
__all__ = ['KDTree', 'KDTree64', 'KDTree32']
{{for name_suffix, INPUT_DTYPE_t, INPUT_DTYPE in implementation_specific_values}}
DOC_DICT{{name_suffix}} = {
'BinaryTree': 'KDTree{{name_suffix}}',
'binary_tree': 'kd_tree{{name_suffix}}',
}
VALID_METRICS{{name_suffix}} = [
'EuclideanDistance{{name_suffix}}',
'ManhattanDistance{{name_suffix}}',
'ChebyshevDistance{{name_suffix}}',
'MinkowskiDistance{{name_suffix}}'
]
{{endfor}}
include "_binary_tree.pxi"
{{for name_suffix, INPUT_DTYPE_t, INPUT_DTYPE in implementation_specific_values}}
# Inherit KDTree{{name_suffix}} from BinaryTree{{name_suffix}}
cdef class KDTree{{name_suffix}}(BinaryTree{{name_suffix}}):
__doc__ = CLASS_DOC.format(**DOC_DICT{{name_suffix}})
pass
{{endfor}}
# ----------------------------------------------------------------------
# The functions below specialized the Binary Tree as a KD Tree
#
# Note that these functions use the concept of "reduced distance".
# The reduced distance, defined for some metrics, is a quantity which
# is more efficient to compute than the distance, but preserves the
# relative rankings of the true distance. For example, the reduced
# distance for the Euclidean metric is the squared-euclidean distance.
# For some metrics, the reduced distance is simply the distance.
{{for name_suffix, INPUT_DTYPE_t, INPUT_DTYPE in implementation_specific_values}}
cdef int allocate_data{{name_suffix}}(
BinaryTree{{name_suffix}} tree,
intp_t n_nodes,
intp_t n_features,
) except -1:
"""Allocate arrays needed for the KD Tree"""
tree.node_bounds = np.zeros((2, n_nodes, n_features), dtype={{INPUT_DTYPE}})
return 0
cdef int init_node{{name_suffix}}(
BinaryTree{{name_suffix}} tree,
NodeData_t[::1] node_data,
intp_t i_node,
intp_t idx_start,
intp_t idx_end,
) except -1:
"""Initialize the node for the dataset stored in tree.data"""
cdef intp_t n_features = tree.data.shape[1]
cdef intp_t i, j
cdef float64_t rad = 0
cdef {{INPUT_DTYPE_t}}* lower_bounds = &tree.node_bounds[0, i_node, 0]
cdef {{INPUT_DTYPE_t}}* upper_bounds = &tree.node_bounds[1, i_node, 0]
cdef const {{INPUT_DTYPE_t}}* data = &tree.data[0, 0]
cdef const intp_t* idx_array = &tree.idx_array[0]
cdef const {{INPUT_DTYPE_t}}* data_row
# determine Node bounds
for j in range(n_features):
lower_bounds[j] = INF
upper_bounds[j] = -INF
# Compute the actual data range. At build time, this is slightly
# slower than using the previously-computed bounds of the parent node,
# but leads to more compact trees and thus faster queries.
for i in range(idx_start, idx_end):
data_row = data + idx_array[i] * n_features
for j in range(n_features):
lower_bounds[j] = fmin(lower_bounds[j], data_row[j])
upper_bounds[j] = fmax(upper_bounds[j], data_row[j])
for j in range(n_features):
if tree.dist_metric.p == INF:
rad = fmax(rad, 0.5 * (upper_bounds[j] - lower_bounds[j]))
else:
rad += pow(0.5 * abs(upper_bounds[j] - lower_bounds[j]),
tree.dist_metric.p)
node_data[i_node].idx_start = idx_start
node_data[i_node].idx_end = idx_end
# The radius will hold the size of the circumscribed hypersphere measured
# with the specified metric: in querying, this is used as a measure of the
# size of each node when deciding which nodes to split.
node_data[i_node].radius = pow(rad, 1. / tree.dist_metric.p)
return 0
cdef float64_t min_rdist{{name_suffix}}(
BinaryTree{{name_suffix}} tree,
intp_t i_node,
const {{INPUT_DTYPE_t}}* pt,
) except -1 nogil:
"""Compute the minimum reduced-distance between a point and a node"""
cdef intp_t n_features = tree.data.shape[1]
cdef float64_t d, d_lo, d_hi, rdist=0.0
cdef intp_t j
if tree.dist_metric.p == INF:
for j in range(n_features):
d_lo = tree.node_bounds[0, i_node, j] - pt[j]
d_hi = pt[j] - tree.node_bounds[1, i_node, j]
d = (d_lo + fabs(d_lo)) + (d_hi + fabs(d_hi))
rdist = fmax(rdist, 0.5 * d)
else:
# here we'll use the fact that x + abs(x) = 2 * max(x, 0)
for j in range(n_features):
d_lo = tree.node_bounds[0, i_node, j] - pt[j]
d_hi = pt[j] - tree.node_bounds[1, i_node, j]
d = (d_lo + fabs(d_lo)) + (d_hi + fabs(d_hi))
rdist += pow(0.5 * d, tree.dist_metric.p)
return rdist
cdef float64_t min_dist{{name_suffix}}(
BinaryTree{{name_suffix}} tree,
intp_t i_node,
const {{INPUT_DTYPE_t}}* pt,
) except -1:
"""Compute the minimum distance between a point and a node"""
if tree.dist_metric.p == INF:
return min_rdist{{name_suffix}}(tree, i_node, pt)
else:
return pow(
min_rdist{{name_suffix}}(tree, i_node, pt),
1. / tree.dist_metric.p
)
cdef float64_t max_rdist{{name_suffix}}(
BinaryTree{{name_suffix}} tree,
intp_t i_node,
const {{INPUT_DTYPE_t}}* pt,
) except -1:
"""Compute the maximum reduced-distance between a point and a node"""
cdef intp_t n_features = tree.data.shape[1]
cdef float64_t d_lo, d_hi, rdist=0.0
cdef intp_t j
if tree.dist_metric.p == INF:
for j in range(n_features):
rdist = fmax(rdist, fabs(pt[j] - tree.node_bounds[0, i_node, j]))
rdist = fmax(rdist, fabs(pt[j] - tree.node_bounds[1, i_node, j]))
else:
for j in range(n_features):
d_lo = fabs(pt[j] - tree.node_bounds[0, i_node, j])
d_hi = fabs(pt[j] - tree.node_bounds[1, i_node, j])
rdist += pow(fmax(d_lo, d_hi), tree.dist_metric.p)
return rdist
cdef float64_t max_dist{{name_suffix}}(
BinaryTree{{name_suffix}} tree,
intp_t i_node,
const {{INPUT_DTYPE_t}}* pt,
) except -1:
"""Compute the maximum distance between a point and a node"""
if tree.dist_metric.p == INF:
return max_rdist{{name_suffix}}(tree, i_node, pt)
else:
return pow(
max_rdist{{name_suffix}}(tree, i_node, pt),
1. / tree.dist_metric.p
)
cdef inline int min_max_dist{{name_suffix}}(
BinaryTree{{name_suffix}} tree,
intp_t i_node,
const {{INPUT_DTYPE_t}}* pt,
float64_t* min_dist,
float64_t* max_dist,
) except -1 nogil:
"""Compute the minimum and maximum distance between a point and a node"""
cdef intp_t n_features = tree.data.shape[1]
cdef float64_t d, d_lo, d_hi
cdef intp_t j
min_dist[0] = 0.0
max_dist[0] = 0.0
if tree.dist_metric.p == INF:
for j in range(n_features):
d_lo = tree.node_bounds[0, i_node, j] - pt[j]
d_hi = pt[j] - tree.node_bounds[1, i_node, j]
d = (d_lo + fabs(d_lo)) + (d_hi + fabs(d_hi))
min_dist[0] = fmax(min_dist[0], 0.5 * d)
max_dist[0] = fmax(max_dist[0], fabs(d_lo))
max_dist[0] = fmax(max_dist[0], fabs(d_hi))
else:
# as above, use the fact that x + abs(x) = 2 * max(x, 0)
for j in range(n_features):
d_lo = tree.node_bounds[0, i_node, j] - pt[j]
d_hi = pt[j] - tree.node_bounds[1, i_node, j]
d = (d_lo + fabs(d_lo)) + (d_hi + fabs(d_hi))
min_dist[0] += pow(0.5 * d, tree.dist_metric.p)
max_dist[0] += pow(fmax(fabs(d_lo), fabs(d_hi)),
tree.dist_metric.p)
min_dist[0] = pow(min_dist[0], 1. / tree.dist_metric.p)
max_dist[0] = pow(max_dist[0], 1. / tree.dist_metric.p)
return 0
cdef inline float64_t min_rdist_dual{{name_suffix}}(
BinaryTree{{name_suffix}} tree1,
intp_t i_node1,
BinaryTree{{name_suffix}} tree2,
intp_t i_node2,
) except -1:
"""Compute the minimum reduced distance between two nodes"""
cdef intp_t n_features = tree1.data.shape[1]
cdef float64_t d, d1, d2, rdist=0.0
cdef intp_t j
if tree1.dist_metric.p == INF:
for j in range(n_features):
d1 = (tree1.node_bounds[0, i_node1, j]
- tree2.node_bounds[1, i_node2, j])
d2 = (tree2.node_bounds[0, i_node2, j]
- tree1.node_bounds[1, i_node1, j])
d = (d1 + fabs(d1)) + (d2 + fabs(d2))
rdist = fmax(rdist, 0.5 * d)
else:
# here we'll use the fact that x + abs(x) = 2 * max(x, 0)
for j in range(n_features):
d1 = (tree1.node_bounds[0, i_node1, j]
- tree2.node_bounds[1, i_node2, j])
d2 = (tree2.node_bounds[0, i_node2, j]
- tree1.node_bounds[1, i_node1, j])
d = (d1 + fabs(d1)) + (d2 + fabs(d2))
rdist += pow(0.5 * d, tree1.dist_metric.p)
return rdist
cdef inline float64_t min_dist_dual{{name_suffix}}(
BinaryTree{{name_suffix}} tree1,
intp_t i_node1,
BinaryTree{{name_suffix}} tree2,
intp_t i_node2,
) except -1:
"""Compute the minimum distance between two nodes"""
return tree1.dist_metric._rdist_to_dist(
min_rdist_dual{{name_suffix}}(tree1, i_node1, tree2, i_node2)
)
cdef inline float64_t max_rdist_dual{{name_suffix}}(
BinaryTree{{name_suffix}} tree1,
intp_t i_node1,
BinaryTree{{name_suffix}} tree2,
intp_t i_node2,
) except -1:
"""Compute the maximum reduced distance between two nodes"""
cdef intp_t n_features = tree1.data.shape[1]
cdef float64_t d1, d2, rdist=0.0
cdef intp_t j
if tree1.dist_metric.p == INF:
for j in range(n_features):
rdist = fmax(rdist, fabs(tree1.node_bounds[0, i_node1, j]
- tree2.node_bounds[1, i_node2, j]))
rdist = fmax(rdist, fabs(tree1.node_bounds[1, i_node1, j]
- tree2.node_bounds[0, i_node2, j]))
else:
for j in range(n_features):
d1 = fabs(tree1.node_bounds[0, i_node1, j]
- tree2.node_bounds[1, i_node2, j])
d2 = fabs(tree1.node_bounds[1, i_node1, j]
- tree2.node_bounds[0, i_node2, j])
rdist += pow(fmax(d1, d2), tree1.dist_metric.p)
return rdist
cdef inline float64_t max_dist_dual{{name_suffix}}(
BinaryTree{{name_suffix}} tree1,
intp_t i_node1,
BinaryTree{{name_suffix}} tree2,
intp_t i_node2,
) except -1:
"""Compute the maximum distance between two nodes"""
return tree1.dist_metric._rdist_to_dist(
max_rdist_dual{{name_suffix}}(tree1, i_node1, tree2, i_node2)
)
{{endfor}}
class KDTree(KDTree64):
__doc__ = CLASS_DOC.format(BinaryTree="KDTree")
pass
{{py:
# Generated file: _kd_tree.pyx
implementation_specific_values = [
# The values are arranged as follows:
#
# name_suffix, INPUT_DTYPE_t, INPUT_DTYPE
#
('64', 'float64_t', 'np.float64'),
('32', 'float32_t', 'np.float32')
]
# By Jake Vanderplas (2013) <jakevdp@cs.washington.edu>
# written for the scikit-learn project
# SPDX-License-Identifier: BSD-3-Clause
}}
__all__ = ['KDTree', 'KDTree64', 'KDTree32']
{{for name_suffix, INPUT_DTYPE_t, INPUT_DTYPE in implementation_specific_values}}
DOC_DICT{{name_suffix}} = {
'BinaryTree': 'KDTree{{name_suffix}}',
'binary_tree': 'kd_tree{{name_suffix}}',
}
VALID_METRICS{{name_suffix}} = [
'EuclideanDistance{{name_suffix}}',
'ManhattanDistance{{name_suffix}}',
'ChebyshevDistance{{name_suffix}}',
'MinkowskiDistance{{name_suffix}}'
]
{{endfor}}
include "_binary_tree.pxi"
{{for name_suffix, INPUT_DTYPE_t, INPUT_DTYPE in implementation_specific_values}}
# Inherit KDTree{{name_suffix}} from BinaryTree{{name_suffix}}
cdef class KDTree{{name_suffix}}(BinaryTree{{name_suffix}}):
__doc__ = CLASS_DOC.format(**DOC_DICT{{name_suffix}})
pass
{{endfor}}
# ----------------------------------------------------------------------
# The functions below specialized the Binary Tree as a KD Tree
#
# Note that these functions use the concept of "reduced distance".
# The reduced distance, defined for some metrics, is a quantity which
# is more efficient to compute than the distance, but preserves the
# relative rankings of the true distance. For example, the reduced
# distance for the Euclidean metric is the squared-euclidean distance.
# For some metrics, the reduced distance is simply the distance.
{{for name_suffix, INPUT_DTYPE_t, INPUT_DTYPE in implementation_specific_values}}
cdef int allocate_data{{name_suffix}}(
BinaryTree{{name_suffix}} tree,
intp_t n_nodes,
intp_t n_features,
) except -1:
"""Allocate arrays needed for the KD Tree"""
tree.node_bounds = np.zeros((2, n_nodes, n_features), dtype={{INPUT_DTYPE}})
return 0
cdef int init_node{{name_suffix}}(
BinaryTree{{name_suffix}} tree,
NodeData_t[::1] node_data,
intp_t i_node,
intp_t idx_start,
intp_t idx_end,
) except -1:
"""Initialize the node for the dataset stored in tree.data"""
cdef intp_t n_features = tree.data.shape[1]
cdef intp_t i, j
cdef float64_t rad = 0
cdef {{INPUT_DTYPE_t}}* lower_bounds = &tree.node_bounds[0, i_node, 0]
cdef {{INPUT_DTYPE_t}}* upper_bounds = &tree.node_bounds[1, i_node, 0]
cdef const {{INPUT_DTYPE_t}}* data = &tree.data[0, 0]
cdef const intp_t* idx_array = &tree.idx_array[0]
cdef const {{INPUT_DTYPE_t}}* data_row
# determine Node bounds
for j in range(n_features):
lower_bounds[j] = INF
upper_bounds[j] = -INF
# Compute the actual data range. At build time, this is slightly
# slower than using the previously-computed bounds of the parent node,
# but leads to more compact trees and thus faster queries.
for i in range(idx_start, idx_end):
data_row = data + idx_array[i] * n_features
for j in range(n_features):
lower_bounds[j] = fmin(lower_bounds[j], data_row[j])
upper_bounds[j] = fmax(upper_bounds[j], data_row[j])
for j in range(n_features):
if tree.dist_metric.p == INF:
rad = fmax(rad, 0.5 * (upper_bounds[j] - lower_bounds[j]))
else:
rad += pow(0.5 * abs(upper_bounds[j] - lower_bounds[j]),
tree.dist_metric.p)
node_data[i_node].idx_start = idx_start
node_data[i_node].idx_end = idx_end
# The radius will hold the size of the circumscribed hypersphere measured
# with the specified metric: in querying, this is used as a measure of the
# size of each node when deciding which nodes to split.
node_data[i_node].radius = pow(rad, 1. / tree.dist_metric.p)
return 0
cdef float64_t min_rdist{{name_suffix}}(
BinaryTree{{name_suffix}} tree,
intp_t i_node,
const {{INPUT_DTYPE_t}}* pt,
) except -1 nogil:
"""Compute the minimum reduced-distance between a point and a node"""
cdef intp_t n_features = tree.data.shape[1]
cdef float64_t d, d_lo, d_hi, rdist=0.0
cdef intp_t j
if tree.dist_metric.p == INF:
for j in range(n_features):
d_lo = tree.node_bounds[0, i_node, j] - pt[j]
d_hi = pt[j] - tree.node_bounds[1, i_node, j]
d = (d_lo + fabs(d_lo)) + (d_hi + fabs(d_hi))
rdist = fmax(rdist, 0.5 * d)
else:
# here we'll use the fact that x + abs(x) = 2 * max(x, 0)
for j in range(n_features):
d_lo = tree.node_bounds[0, i_node, j] - pt[j]
d_hi = pt[j] - tree.node_bounds[1, i_node, j]
d = (d_lo + fabs(d_lo)) + (d_hi + fabs(d_hi))
rdist += pow(0.5 * d, tree.dist_metric.p)
return rdist
cdef float64_t min_dist{{name_suffix}}(
BinaryTree{{name_suffix}} tree,
intp_t i_node,
const {{INPUT_DTYPE_t}}* pt,
) except -1:
"""Compute the minimum distance between a point and a node"""
if tree.dist_metric.p == INF:
return min_rdist{{name_suffix}}(tree, i_node, pt)
else:
return pow(
min_rdist{{name_suffix}}(tree, i_node, pt),
1. / tree.dist_metric.p
)
cdef float64_t max_rdist{{name_suffix}}(
BinaryTree{{name_suffix}} tree,
intp_t i_node,
const {{INPUT_DTYPE_t}}* pt,
) except -1:
"""Compute the maximum reduced-distance between a point and a node"""
cdef intp_t n_features = tree.data.shape[1]
cdef float64_t d_lo, d_hi, rdist=0.0
cdef intp_t j
if tree.dist_metric.p == INF:
for j in range(n_features):
rdist = fmax(rdist, fabs(pt[j] - tree.node_bounds[0, i_node, j]))
rdist = fmax(rdist, fabs(pt[j] - tree.node_bounds[1, i_node, j]))
else:
for j in range(n_features):
d_lo = fabs(pt[j] - tree.node_bounds[0, i_node, j])
d_hi = fabs(pt[j] - tree.node_bounds[1, i_node, j])
rdist += pow(fmax(d_lo, d_hi), tree.dist_metric.p)
return rdist
cdef float64_t max_dist{{name_suffix}}(
BinaryTree{{name_suffix}} tree,
intp_t i_node,
const {{INPUT_DTYPE_t}}* pt,
) except -1:
"""Compute the maximum distance between a point and a node"""
if tree.dist_metric.p == INF:
return max_rdist{{name_suffix}}(tree, i_node, pt)
else:
return pow(
max_rdist{{name_suffix}}(tree, i_node, pt),
1. / tree.dist_metric.p
)
cdef inline int min_max_dist{{name_suffix}}(
BinaryTree{{name_suffix}} tree,
intp_t i_node,
const {{INPUT_DTYPE_t}}* pt,
float64_t* min_dist,
float64_t* max_dist,
) except -1 nogil:
"""Compute the minimum and maximum distance between a point and a node"""
cdef intp_t n_features = tree.data.shape[1]
cdef float64_t d, d_lo, d_hi
cdef intp_t j
min_dist[0] = 0.0
max_dist[0] = 0.0
if tree.dist_metric.p == INF:
for j in range(n_features):
d_lo = tree.node_bounds[0, i_node, j] - pt[j]
d_hi = pt[j] - tree.node_bounds[1, i_node, j]
d = (d_lo + fabs(d_lo)) + (d_hi + fabs(d_hi))
min_dist[0] = fmax(min_dist[0], 0.5 * d)
max_dist[0] = fmax(max_dist[0], fabs(d_lo))
max_dist[0] = fmax(max_dist[0], fabs(d_hi))
else:
# as above, use the fact that x + abs(x) = 2 * max(x, 0)
for j in range(n_features):
d_lo = tree.node_bounds[0, i_node, j] - pt[j]
d_hi = pt[j] - tree.node_bounds[1, i_node, j]
d = (d_lo + fabs(d_lo)) + (d_hi + fabs(d_hi))
min_dist[0] += pow(0.5 * d, tree.dist_metric.p)
max_dist[0] += pow(fmax(fabs(d_lo), fabs(d_hi)),
tree.dist_metric.p)
min_dist[0] = pow(min_dist[0], 1. / tree.dist_metric.p)
max_dist[0] = pow(max_dist[0], 1. / tree.dist_metric.p)
return 0
cdef inline float64_t min_rdist_dual{{name_suffix}}(
BinaryTree{{name_suffix}} tree1,
intp_t i_node1,
BinaryTree{{name_suffix}} tree2,
intp_t i_node2,
) except -1:
"""Compute the minimum reduced distance between two nodes"""
cdef intp_t n_features = tree1.data.shape[1]
cdef float64_t d, d1, d2, rdist=0.0
cdef intp_t j
if tree1.dist_metric.p == INF:
for j in range(n_features):
d1 = (tree1.node_bounds[0, i_node1, j]
- tree2.node_bounds[1, i_node2, j])
d2 = (tree2.node_bounds[0, i_node2, j]
- tree1.node_bounds[1, i_node1, j])
d = (d1 + fabs(d1)) + (d2 + fabs(d2))
rdist = fmax(rdist, 0.5 * d)
else:
# here we'll use the fact that x + abs(x) = 2 * max(x, 0)
for j in range(n_features):
d1 = (tree1.node_bounds[0, i_node1, j]
- tree2.node_bounds[1, i_node2, j])
d2 = (tree2.node_bounds[0, i_node2, j]
- tree1.node_bounds[1, i_node1, j])
d = (d1 + fabs(d1)) + (d2 + fabs(d2))
rdist += pow(0.5 * d, tree1.dist_metric.p)
return rdist
cdef inline float64_t min_dist_dual{{name_suffix}}(
BinaryTree{{name_suffix}} tree1,
intp_t i_node1,
BinaryTree{{name_suffix}} tree2,
intp_t i_node2,
) except -1:
"""Compute the minimum distance between two nodes"""
return tree1.dist_metric._rdist_to_dist(
min_rdist_dual{{name_suffix}}(tree1, i_node1, tree2, i_node2)
)
cdef inline float64_t max_rdist_dual{{name_suffix}}(
BinaryTree{{name_suffix}} tree1,
intp_t i_node1,
BinaryTree{{name_suffix}} tree2,
intp_t i_node2,
) except -1:
"""Compute the maximum reduced distance between two nodes"""
cdef intp_t n_features = tree1.data.shape[1]
cdef float64_t d1, d2, rdist=0.0
cdef intp_t j
if tree1.dist_metric.p == INF:
for j in range(n_features):
rdist = fmax(rdist, fabs(tree1.node_bounds[0, i_node1, j]
- tree2.node_bounds[1, i_node2, j]))
rdist = fmax(rdist, fabs(tree1.node_bounds[1, i_node1, j]
- tree2.node_bounds[0, i_node2, j]))
else:
for j in range(n_features):
d1 = fabs(tree1.node_bounds[0, i_node1, j]
- tree2.node_bounds[1, i_node2, j])
d2 = fabs(tree1.node_bounds[1, i_node1, j]
- tree2.node_bounds[0, i_node2, j])
rdist += pow(fmax(d1, d2), tree1.dist_metric.p)
return rdist
cdef inline float64_t max_dist_dual{{name_suffix}}(
BinaryTree{{name_suffix}} tree1,
intp_t i_node1,
BinaryTree{{name_suffix}} tree2,
intp_t i_node2,
) except -1:
"""Compute the maximum distance between two nodes"""
return tree1.dist_metric._rdist_to_dist(
max_rdist_dual{{name_suffix}}(tree1, i_node1, tree2, i_node2)
)
{{endfor}}
class KDTree(KDTree64):
__doc__ = CLASS_DOC.format(BinaryTree="KDTree")
pass
@@ -1,10 +1,10 @@
from cython cimport floating
from ..utils._typedefs cimport float64_t, intp_t
cdef int partition_node_indices(
const floating *data,
intp_t *node_indices,
intp_t split_dim,
intp_t split_index,
intp_t n_features,
intp_t n_points) except -1
from cython cimport floating
from ..utils._typedefs cimport float64_t, intp_t
cdef int partition_node_indices(
const floating *data,
intp_t *node_indices,
intp_t split_dim,
intp_t split_index,
intp_t n_features,
intp_t n_points) except -1
@@ -1,122 +1,122 @@
# BinaryTrees rely on partial sorts to partition their nodes during their
# initialisation.
#
# The C++ std library exposes nth_element, an efficient partial sort for this
# situation which has a linear time complexity as well as the best performances.
#
# To use std::algorithm::nth_element, a few fixture are defined using Cython:
# - partition_node_indices, a Cython function used in BinaryTrees, that calls
# - partition_node_indices_inner, a C++ function that wraps nth_element and uses
# - an IndexComparator to state how to compare KDTrees' indices
#
# IndexComparator has been defined so that partial sorts are stable with
# respect to the nodes initial indices.
#
# See for reference:
# - https://en.cppreference.com/w/cpp/algorithm/nth_element.
# - https://github.com/scikit-learn/scikit-learn/pull/11103
# - https://github.com/scikit-learn/scikit-learn/pull/19473
from cython cimport floating
cdef extern from *:
"""
#include <algorithm>
template<class D, class I>
class IndexComparator {
private:
const D *data;
I split_dim, n_features;
public:
IndexComparator(const D *data, const I &split_dim, const I &n_features):
data(data), split_dim(split_dim), n_features(n_features) {}
bool operator()(const I &a, const I &b) const {
D a_value = data[a * n_features + split_dim];
D b_value = data[b * n_features + split_dim];
return a_value == b_value ? a < b : a_value < b_value;
}
};
template<class D, class I>
void partition_node_indices_inner(
const D *data,
I *node_indices,
const I &split_dim,
const I &split_index,
const I &n_features,
const I &n_points) {
IndexComparator<D, I> index_comparator(data, split_dim, n_features);
std::nth_element(
node_indices,
node_indices + split_index,
node_indices + n_points,
index_comparator);
}
"""
void partition_node_indices_inner[D, I](
const D *data,
I *node_indices,
I split_dim,
I split_index,
I n_features,
I n_points) except +
cdef int partition_node_indices(
const floating *data,
intp_t *node_indices,
intp_t split_dim,
intp_t split_index,
intp_t n_features,
intp_t n_points) except -1:
"""Partition points in the node into two equal-sized groups.
Upon return, the values in node_indices will be rearranged such that
(assuming numpy-style indexing):
data[node_indices[0:split_index], split_dim]
<= data[node_indices[split_index], split_dim]
and
data[node_indices[split_index], split_dim]
<= data[node_indices[split_index:n_points], split_dim]
The algorithm is essentially a partial in-place quicksort around a
set pivot.
Parameters
----------
data : double pointer
Pointer to a 2D array of the training data, of shape [N, n_features].
N must be greater than any of the values in node_indices.
node_indices : int pointer
Pointer to a 1D array of length n_points. This lists the indices of
each of the points within the current node. This will be modified
in-place.
split_dim : int
the dimension on which to split. This will usually be computed via
the routine ``find_node_split_dim``.
split_index : int
the index within node_indices around which to split the points.
n_features: int
the number of features (i.e columns) in the 2D array pointed by data.
n_points : int
the length of node_indices. This is also the number of points in
the original dataset.
Returns
-------
status : int
integer exit status. On return, the contents of node_indices are
modified as noted above.
"""
partition_node_indices_inner(
data,
node_indices,
split_dim,
split_index,
n_features,
n_points)
return 0
# BinaryTrees rely on partial sorts to partition their nodes during their
# initialisation.
#
# The C++ std library exposes nth_element, an efficient partial sort for this
# situation which has a linear time complexity as well as the best performances.
#
# To use std::algorithm::nth_element, a few fixture are defined using Cython:
# - partition_node_indices, a Cython function used in BinaryTrees, that calls
# - partition_node_indices_inner, a C++ function that wraps nth_element and uses
# - an IndexComparator to state how to compare KDTrees' indices
#
# IndexComparator has been defined so that partial sorts are stable with
# respect to the nodes initial indices.
#
# See for reference:
# - https://en.cppreference.com/w/cpp/algorithm/nth_element.
# - https://github.com/scikit-learn/scikit-learn/pull/11103
# - https://github.com/scikit-learn/scikit-learn/pull/19473
from cython cimport floating
cdef extern from *:
"""
#include <algorithm>
template<class D, class I>
class IndexComparator {
private:
const D *data;
I split_dim, n_features;
public:
IndexComparator(const D *data, const I &split_dim, const I &n_features):
data(data), split_dim(split_dim), n_features(n_features) {}
bool operator()(const I &a, const I &b) const {
D a_value = data[a * n_features + split_dim];
D b_value = data[b * n_features + split_dim];
return a_value == b_value ? a < b : a_value < b_value;
}
};
template<class D, class I>
void partition_node_indices_inner(
const D *data,
I *node_indices,
const I &split_dim,
const I &split_index,
const I &n_features,
const I &n_points) {
IndexComparator<D, I> index_comparator(data, split_dim, n_features);
std::nth_element(
node_indices,
node_indices + split_index,
node_indices + n_points,
index_comparator);
}
"""
void partition_node_indices_inner[D, I](
const D *data,
I *node_indices,
I split_dim,
I split_index,
I n_features,
I n_points) except +
cdef int partition_node_indices(
const floating *data,
intp_t *node_indices,
intp_t split_dim,
intp_t split_index,
intp_t n_features,
intp_t n_points) except -1:
"""Partition points in the node into two equal-sized groups.
Upon return, the values in node_indices will be rearranged such that
(assuming numpy-style indexing):
data[node_indices[0:split_index], split_dim]
<= data[node_indices[split_index], split_dim]
and
data[node_indices[split_index], split_dim]
<= data[node_indices[split_index:n_points], split_dim]
The algorithm is essentially a partial in-place quicksort around a
set pivot.
Parameters
----------
data : double pointer
Pointer to a 2D array of the training data, of shape [N, n_features].
N must be greater than any of the values in node_indices.
node_indices : int pointer
Pointer to a 1D array of length n_points. This lists the indices of
each of the points within the current node. This will be modified
in-place.
split_dim : int
the dimension on which to split. This will usually be computed via
the routine ``find_node_split_dim``.
split_index : int
the index within node_indices around which to split the points.
n_features: int
the number of features (i.e columns) in the 2D array pointed by data.
n_points : int
the length of node_indices. This is also the number of points in
the original dataset.
Returns
-------
status : int
integer exit status. On return, the contents of node_indices are
modified as noted above.
"""
partition_node_indices_inner(
data,
node_indices,
split_dim,
split_index,
n_features,
n_points)
return 0
@@ -1,92 +1,92 @@
# Authors: The scikit-learn developers
# SPDX-License-Identifier: BSD-3-Clause
# See quad_tree.pyx for details.
cimport numpy as cnp
from ..utils._typedefs cimport float32_t, intp_t
# This is effectively an ifdef statement in Cython
# It allows us to write printf debugging lines
# and remove them at compile time
cdef enum:
DEBUGFLAG = 0
cdef float EPSILON = 1e-6
# XXX: Careful to not change the order of the arguments. It is important to
# have is_leaf and max_width consecutive as it permits to avoid padding by
# the compiler and keep the size coherent for both C and numpy data structures.
cdef struct Cell:
# Base storage structure for cells in a QuadTree object
# Tree structure
intp_t parent # Parent cell of this cell
intp_t[8] children # Array pointing to children of this cell
# Cell description
intp_t cell_id # Id of the cell in the cells array in the Tree
intp_t point_index # Index of the point at this cell (only defined
# # in non empty leaf)
bint is_leaf # Does this cell have children?
float32_t squared_max_width # Squared value of the maximum width w
intp_t depth # Depth of the cell in the tree
intp_t cumulative_size # Number of points included in the subtree with
# # this cell as a root.
# Internal constants
float32_t[3] center # Store the center for quick split of cells
float32_t[3] barycenter # Keep track of the center of mass of the cell
# Cell boundaries
float32_t[3] min_bounds # Inferior boundaries of this cell (inclusive)
float32_t[3] max_bounds # Superior boundaries of this cell (exclusive)
cdef class _QuadTree:
# The QuadTree object is a quad tree structure constructed by inserting
# recursively points in the tree and splitting cells in 4 so that each
# leaf cell contains at most one point.
# This structure also handle 3D data, inserted in trees with 8 children
# for each node.
# Parameters of the tree
cdef public int n_dimensions # Number of dimensions in X
cdef public int verbose # Verbosity of the output
cdef intp_t n_cells_per_cell # Number of children per node. (2 ** n_dimension)
# Tree inner structure
cdef public intp_t max_depth # Max depth of the tree
cdef public intp_t cell_count # Counter for node IDs
cdef public intp_t capacity # Capacity of tree, in terms of nodes
cdef public intp_t n_points # Total number of points
cdef Cell* cells # Array of nodes
# Point insertion methods
cdef int insert_point(self, float32_t[3] point, intp_t point_index,
intp_t cell_id=*) except -1 nogil
cdef intp_t _insert_point_in_new_child(self, float32_t[3] point, Cell* cell,
intp_t point_index, intp_t size=*
) noexcept nogil
cdef intp_t _select_child(self, float32_t[3] point, Cell* cell) noexcept nogil
cdef bint _is_duplicate(self, float32_t[3] point1, float32_t[3] point2) noexcept nogil
# Create a summary of the Tree compare to a query point
cdef long summarize(self, float32_t[3] point, float32_t* results,
float squared_theta=*, intp_t cell_id=*, long idx=*
) noexcept nogil
# Internal cell initialization methods
cdef void _init_cell(self, Cell* cell, intp_t parent, intp_t depth) noexcept nogil
cdef void _init_root(self, float32_t[3] min_bounds, float32_t[3] max_bounds
) noexcept nogil
# Private methods
cdef int _check_point_in_cell(self, float32_t[3] point, Cell* cell
) except -1 nogil
# Private array manipulation to manage the ``cells`` array
cdef int _resize(self, intp_t capacity) except -1 nogil
cdef int _resize_c(self, intp_t capacity=*) except -1 nogil
cdef int _get_cell(self, float32_t[3] point, intp_t cell_id=*) except -1 nogil
cdef Cell[:] _get_cell_ndarray(self)
# Authors: The scikit-learn developers
# SPDX-License-Identifier: BSD-3-Clause
# See quad_tree.pyx for details.
cimport numpy as cnp
from ..utils._typedefs cimport float32_t, intp_t
# This is effectively an ifdef statement in Cython
# It allows us to write printf debugging lines
# and remove them at compile time
cdef enum:
DEBUGFLAG = 0
cdef float EPSILON = 1e-6
# XXX: Careful to not change the order of the arguments. It is important to
# have is_leaf and max_width consecutive as it permits to avoid padding by
# the compiler and keep the size coherent for both C and numpy data structures.
cdef struct Cell:
# Base storage structure for cells in a QuadTree object
# Tree structure
intp_t parent # Parent cell of this cell
intp_t[8] children # Array pointing to children of this cell
# Cell description
intp_t cell_id # Id of the cell in the cells array in the Tree
intp_t point_index # Index of the point at this cell (only defined
# # in non empty leaf)
bint is_leaf # Does this cell have children?
float32_t squared_max_width # Squared value of the maximum width w
intp_t depth # Depth of the cell in the tree
intp_t cumulative_size # Number of points included in the subtree with
# # this cell as a root.
# Internal constants
float32_t[3] center # Store the center for quick split of cells
float32_t[3] barycenter # Keep track of the center of mass of the cell
# Cell boundaries
float32_t[3] min_bounds # Inferior boundaries of this cell (inclusive)
float32_t[3] max_bounds # Superior boundaries of this cell (exclusive)
cdef class _QuadTree:
# The QuadTree object is a quad tree structure constructed by inserting
# recursively points in the tree and splitting cells in 4 so that each
# leaf cell contains at most one point.
# This structure also handle 3D data, inserted in trees with 8 children
# for each node.
# Parameters of the tree
cdef public int n_dimensions # Number of dimensions in X
cdef public int verbose # Verbosity of the output
cdef intp_t n_cells_per_cell # Number of children per node. (2 ** n_dimension)
# Tree inner structure
cdef public intp_t max_depth # Max depth of the tree
cdef public intp_t cell_count # Counter for node IDs
cdef public intp_t capacity # Capacity of tree, in terms of nodes
cdef public intp_t n_points # Total number of points
cdef Cell* cells # Array of nodes
# Point insertion methods
cdef int insert_point(self, float32_t[3] point, intp_t point_index,
intp_t cell_id=*) except -1 nogil
cdef intp_t _insert_point_in_new_child(self, float32_t[3] point, Cell* cell,
intp_t point_index, intp_t size=*
) noexcept nogil
cdef intp_t _select_child(self, float32_t[3] point, Cell* cell) noexcept nogil
cdef bint _is_duplicate(self, float32_t[3] point1, float32_t[3] point2) noexcept nogil
# Create a summary of the Tree compare to a query point
cdef long summarize(self, float32_t[3] point, float32_t* results,
float squared_theta=*, intp_t cell_id=*, long idx=*
) noexcept nogil
# Internal cell initialization methods
cdef void _init_cell(self, Cell* cell, intp_t parent, intp_t depth) noexcept nogil
cdef void _init_root(self, float32_t[3] min_bounds, float32_t[3] max_bounds
) noexcept nogil
# Private methods
cdef int _check_point_in_cell(self, float32_t[3] point, Cell* cell
) except -1 nogil
# Private array manipulation to manage the ``cells`` array
cdef int _resize(self, intp_t capacity) except -1 nogil
cdef int _resize_c(self, intp_t capacity=*) except -1 nogil
cdef int _get_cell(self, float32_t[3] point, intp_t cell_id=*) except -1 nogil
cdef Cell[:] _get_cell_ndarray(self)
File diff suppressed because it is too large Load Diff
@@ -1,53 +1,53 @@
_binary_tree_pxi = custom_target(
'_binary_tree_pxi',
output: '_binary_tree.pxi',
input: '_binary_tree.pxi.tp',
command: [tempita, '@INPUT@', '-o', '@OUTDIR@'],
)
# .pyx is generated so this is needed to make Cython compilation work. The pxi
# file is included avoid "missing dependency paths" with ninja -t missindeps
neighbors_cython_tree = [
fs.copyfile('__init__.py'),
fs.copyfile('_partition_nodes.pxd'),
_binary_tree_pxi,
]
name_list = ['_ball_tree', '_kd_tree']
foreach name: name_list
pyx = custom_target(
name + '_pyx',
output: name + '.pyx',
input: name + '.pyx.tp',
command: [tempita, '@INPUT@', '-o', '@OUTDIR@'],
# TODO in principle this should go in py.exension_module below. This is
# temporary work-around for dependency issue with .pyx.tp files. For more
# details, see https://github.com/mesonbuild/meson/issues/13212
depends: [neighbors_cython_tree, utils_cython_tree, metrics_cython_tree],
)
py.extension_module(
name,
cython_gen.process(pyx),
dependencies: [np_dep],
subdir: 'sklearn/neighbors',
install: true
)
endforeach
neighbors_extension_metadata = {
'_partition_nodes':
{'sources': [cython_gen_cpp.process('_partition_nodes.pyx')],
'dependencies': [np_dep]},
'_quad_tree': {'sources': [cython_gen.process('_quad_tree.pyx')], 'dependencies': [np_dep]},
}
foreach ext_name, ext_dict : neighbors_extension_metadata
py.extension_module(
ext_name,
[ext_dict.get('sources'), utils_cython_tree],
dependencies: ext_dict.get('dependencies'),
subdir: 'sklearn/neighbors',
install: true
)
endforeach
_binary_tree_pxi = custom_target(
'_binary_tree_pxi',
output: '_binary_tree.pxi',
input: '_binary_tree.pxi.tp',
command: [tempita, '@INPUT@', '-o', '@OUTDIR@'],
)
# .pyx is generated so this is needed to make Cython compilation work. The pxi
# file is included avoid "missing dependency paths" with ninja -t missindeps
neighbors_cython_tree = [
fs.copyfile('__init__.py'),
fs.copyfile('_partition_nodes.pxd'),
_binary_tree_pxi,
]
name_list = ['_ball_tree', '_kd_tree']
foreach name: name_list
pyx = custom_target(
name + '_pyx',
output: name + '.pyx',
input: name + '.pyx.tp',
command: [tempita, '@INPUT@', '-o', '@OUTDIR@'],
# TODO in principle this should go in py.exension_module below. This is
# temporary work-around for dependency issue with .pyx.tp files. For more
# details, see https://github.com/mesonbuild/meson/issues/13212
depends: [neighbors_cython_tree, utils_cython_tree, metrics_cython_tree],
)
py.extension_module(
name,
cython_gen.process(pyx),
dependencies: [np_dep],
subdir: 'sklearn/neighbors',
install: true
)
endforeach
neighbors_extension_metadata = {
'_partition_nodes':
{'sources': [cython_gen_cpp.process('_partition_nodes.pyx')],
'dependencies': [np_dep]},
'_quad_tree': {'sources': [cython_gen.process('_quad_tree.pyx')], 'dependencies': [np_dep]},
}
foreach ext_name, ext_dict : neighbors_extension_metadata
py.extension_module(
ext_name,
[ext_dict.get('sources'), utils_cython_tree],
dependencies: ext_dict.get('dependencies'),
subdir: 'sklearn/neighbors',
install: true
)
endforeach