"""
Implementation of a dummy score using
:epkg:`cdist`.
:githublink:`%|py|6`
"""
import numpy
from onnx import onnx_pb as onnx_proto
from scipy.spatial.distance import cdist
[docs]def score_cdist_sum(expected_values, predictions,
metric='sqeuclidean', p=None):
"""
Computes the sum of pairwise distances between
*expected_values* and *predictions*. It has no
particular purpose except the one of converting
a scorer into ONNX.
:param expected_values: expected_values
:param predictions: predictions
:param metric: see function :epkg:`cdist`
:param p: see function :epkg:`cdist`
:return: some of the pairwise distances
:githublink:`%|py|24`
"""
dist = cdist(expected_values, predictions, metric=metric, p=p)
return numpy.sum(dist, axis=1)
[docs]def convert_score_cdist_sum(scope, operator, container):
"""
Converts function :func:`score_cdist_sum <mlprodict.onnx_conv.scorers.cdist_score.score_cdist_sum>` into :epkg:`ONNX`.
:githublink:`%|py|32`
"""
op = operator.raw_operator
if op._fct != score_cdist_sum: # pylint: disable=W0143
raise RuntimeError( # pragma: no cover
"The wrong converter was called {} != {}.".format(
op._fct, score_cdist_sum))
from skl2onnx.algebra.complex_functions import onnx_cdist
from skl2onnx.algebra.onnx_ops import OnnxReduceSumApi11 # pylint: disable=E0611
from skl2onnx.common.data_types import guess_numpy_type
X = operator.inputs[0]
Y = operator.inputs[1]
out = operator.outputs
opv = container.target_opset
dtype = guess_numpy_type(operator.inputs[0].type)
out = operator.outputs
options = container.get_options(score_cdist_sum, dict(cdist=None))
kwargs = op.kwargs
if options.get('cdist', None) == 'single-node':
attrs = kwargs
cdist_name = scope.get_unique_variable_name('cdist')
container.add_node('CDist', [X.full_name, Y.full_name], cdist_name,
op_domain='mlprodict', name=scope.get_unique_operator_name('CDist'),
**attrs)
if container.target_opset < 13:
container.add_node('ReduceSum', [cdist_name], out[0].full_name,
axes=[1], keepdims=0,
name=scope.get_unique_operator_name('ReduceSum'))
else:
axis_name = scope.get_unique_variable_name('axis')
container.add_initializer(
axis_name, onnx_proto.TensorProto.INT64, [1], [1]) # pylint: disable=E1101
container.add_node(
'ReduceSum', [cdist_name, axis_name],
out[0].full_name, keepdims=0,
name=scope.get_unique_operator_name('ReduceSum'))
else:
metric = kwargs['metric']
if metric == 'minkowski':
dists = onnx_cdist(X, Y, dtype=dtype, op_version=opv,
metric=metric, p=kwargs.get('p', 2))
else:
dists = onnx_cdist(X, Y, dtype=dtype, op_version=opv,
metric=kwargs['metric'])
res = OnnxReduceSumApi11(dists, axes=[1], keepdims=0,
output_names=[out[0].full_name],
op_version=opv)
res.add_to(scope, container)