Coverage for mlprodict/onnx_conv/scorers/cdist_score.py: 100%

37 statements  

« prev     ^ index     » next       coverage.py v7.1.0, created at 2023-02-04 02:28 +0100

1""" 

2@file 

3@brief Implementation of a dummy score using 

4:epkg:`cdist`. 

5""" 

6import numpy 

7from onnx import onnx_pb as onnx_proto 

8from scipy.spatial.distance import cdist 

9 

10 

11def score_cdist_sum(expected_values, predictions, 

12 metric='sqeuclidean', p=None): 

13 """ 

14 Computes the sum of pairwise distances between 

15 *expected_values* and *predictions*. It has no 

16 particular purpose except the one of converting 

17 a scorer into ONNX. 

18 

19 @param expected_values expected_values 

20 @param predictions predictions 

21 @param metric see function :epkg:`cdist` 

22 @param p see function :epkg:`cdist` 

23 @return some of the pairwise distances 

24 """ 

25 if p is None: 

26 dist = cdist(expected_values, predictions, metric=metric) 

27 else: 

28 dist = cdist(expected_values, predictions, metric=metric, p=p) 

29 return numpy.sum(dist, axis=1) 

30 

31 

32def convert_score_cdist_sum(scope, operator, container): 

33 """ 

34 Converts function @see fn score_cdist_sum into :epkg:`ONNX`. 

35 """ 

36 op = operator.raw_operator 

37 if op._fct != score_cdist_sum: # pylint: disable=W0143 

38 raise RuntimeError( # pragma: no cover 

39 f"The wrong converter was called {op._fct} != {score_cdist_sum}.") 

40 

41 from skl2onnx.algebra.complex_functions import onnx_cdist 

42 from skl2onnx.algebra.onnx_ops import OnnxReduceSumApi11 # pylint: disable=E0611 

43 from skl2onnx.common.data_types import guess_numpy_type 

44 

45 X = operator.inputs[0] 

46 Y = operator.inputs[1] 

47 out = operator.outputs 

48 opv = container.target_opset 

49 dtype = guess_numpy_type(operator.inputs[0].type) 

50 out = operator.outputs 

51 

52 options = container.get_options(score_cdist_sum, dict(cdist=None)) 

53 

54 kwargs = op.kwargs 

55 

56 if options.get('cdist', None) == 'single-node': 

57 attrs = kwargs 

58 cdist_name = scope.get_unique_variable_name('cdist') 

59 container.add_node('CDist', [X.full_name, Y.full_name], cdist_name, 

60 op_domain='mlprodict', name=scope.get_unique_operator_name('CDist'), 

61 **attrs) 

62 if container.target_opset < 13: 

63 container.add_node('ReduceSum', [cdist_name], out[0].full_name, 

64 axes=[1], keepdims=0, 

65 name=scope.get_unique_operator_name('ReduceSum')) 

66 else: 

67 axis_name = scope.get_unique_variable_name('axis') 

68 container.add_initializer( 

69 axis_name, onnx_proto.TensorProto.INT64, [1], [1]) # pylint: disable=E1101 

70 container.add_node( 

71 'ReduceSum', [cdist_name, axis_name], 

72 out[0].full_name, keepdims=0, 

73 name=scope.get_unique_operator_name('ReduceSum')) 

74 else: 

75 metric = kwargs['metric'] 

76 if metric == 'minkowski': 

77 dists = onnx_cdist(X, Y, dtype=dtype, op_version=opv, 

78 metric=metric, p=kwargs.get('p', 2)) 

79 else: 

80 dists = onnx_cdist(X, Y, dtype=dtype, op_version=opv, 

81 metric=kwargs['metric']) 

82 

83 res = OnnxReduceSumApi11(dists, axes=[1], keepdims=0, 

84 output_names=[out[0].full_name], 

85 op_version=opv) 

86 res.add_to(scope, container)