From 386bccb6e769c4f228f93f5bb29871bb91df875a Mon Sep 17 00:00:00 2001 From: Atanas Dimitrov <70822030+neNasko1@users.noreply.github.com> Date: Tue, 7 Jan 2025 01:14:54 +0200 Subject: [PATCH] Remove unused `jinja` templates (#199) * Remove outdated jinja templates Signed-off-by: neNasko1 * Remove even more code Signed-off-by: neNasko1 * Fix license * Update license * Fix license * Revert change on category mapper and tree ensemble * Fix generation Signed-off-by: Atanas Dimitrov --------- Signed-off-by: neNasko1 Signed-off-by: Atanas Dimitrov Co-authored-by: Aditya Goel <48102515+adityagoel4512@users.noreply.github.com> --- src/spox/opset/ai/onnx/ml/v3.py | 17 ------- src/spox/opset/ai/onnx/v17.py | 28 +---------- .../test_tree_ensemble_classifier.py | 6 +-- tools/generate_opset.py | 14 ++---- tools/templates/extras/promote.jinja2 | 25 ---------- tools/templates/extras/xif.jinja2 | 25 ---------- tools/templates/extras/xloop.jinja2 | 33 ------------- .../type_inference/binarizer1.jinja2 | 1 - .../type_inference/compress11.jinja2 | 17 ------- tools/templates/type_inference/if16.jinja2 | 16 ------ tools/templates/type_inference/loop16.jinja2 | 49 ------------------- .../templates/type_inference/onehot11.jinja2 | 21 -------- .../type_inference/onehotencoder1.jinja2 | 14 ------ 13 files changed, 9 insertions(+), 257 deletions(-) delete mode 100644 tools/templates/extras/promote.jinja2 delete mode 100644 tools/templates/extras/xif.jinja2 delete mode 100644 tools/templates/extras/xloop.jinja2 delete mode 100644 tools/templates/type_inference/binarizer1.jinja2 delete mode 100644 tools/templates/type_inference/compress11.jinja2 delete mode 100644 tools/templates/type_inference/if16.jinja2 delete mode 100644 tools/templates/type_inference/loop16.jinja2 delete mode 100644 tools/templates/type_inference/onehot11.jinja2 delete mode 100644 tools/templates/type_inference/onehotencoder1.jinja2 diff --git a/src/spox/opset/ai/onnx/ml/v3.py b/src/spox/opset/ai/onnx/ml/v3.py index ce292aa9..926f5ed1 100644 --- a/src/spox/opset/ai/onnx/ml/v3.py +++ b/src/spox/opset/ai/onnx/ml/v3.py @@ -76,9 +76,6 @@ class Inputs(BaseInputs): class Outputs(BaseOutputs): Y: _VarInfo - def infer_output_types(self, input_prop_values: PropDict) -> dict[str, Type]: - return {"Y": self.inputs.X.type} if self.inputs.X.type is not None else {} - op_type = OpType("Binarizer", "ai.onnx.ml", 1) attrs: Attributes @@ -375,20 +372,6 @@ class Inputs(BaseInputs): class Outputs(BaseOutputs): Y: _VarInfo - def infer_output_types(self, input_prop_values: PropDict) -> dict[str, Type]: - if not self.inputs.fully_typed: - return {} - if self.attrs.cats_int64s: - n_encodings = len(self.attrs.cats_int64s.value) - elif self.attrs.cats_strings: - n_encodings = len(self.attrs.cats_strings.value) - else: - raise InferenceError( - "Either `cats_int64s` or `cats_strings` attributes must be set." - ) - shape = (*self.inputs.X.unwrap_tensor().shape, n_encodings) # type: ignore - return {"Y": Tensor(dtype=np.float32, shape=shape)} - op_type = OpType("OneHotEncoder", "ai.onnx.ml", 1) attrs: Attributes diff --git a/src/spox/opset/ai/onnx/v17.py b/src/spox/opset/ai/onnx/v17.py index b0ab6a8d..0ade211b 100644 --- a/src/spox/opset/ai/onnx/v17.py +++ b/src/spox/opset/ai/onnx/v17.py @@ -28,7 +28,7 @@ from spox._fields import BaseAttributes, BaseInputs, BaseOutputs from spox._graph import Graph, subgraph from spox._node import OpType -from spox._standard import InferenceError, StandardNode +from spox._standard import StandardNode from spox._type_inference_utils import loop_erase_shape_info from spox._type_system import Sequence as SpoxSequence from spox._type_system import Tensor, Type @@ -500,32 +500,6 @@ class Inputs(BaseInputs): class Outputs(BaseOutputs): output: _VarInfo - def infer_output_types(self, input_prop_values: PropDict) -> dict[str, Type]: - self.infer_output_types_onnx(input_prop_values) - inp, cond = ( - self.inputs.input.unwrap_tensor(), - self.inputs.condition.unwrap_tensor(), - ) - if not inp.shape: - return {"output": Tensor(inp.dtype, None)} - if cond.dtype != np.dtype(bool): - raise InferenceError("Compress input 'condition' must be a boolean dtype.") - if cond.shape and len(cond.shape) != 1: - raise InferenceError( - "Compress input 'condition' must be a vector (of rank 1)." - ) - if self.attrs.axis is not None: - shape = list(inp.shape) - axis = self.attrs.axis.value - if not (-len(shape) <= axis < len(shape)): - raise InferenceError( - f"Compress attribute 'axis' must in range [-rank, rank-1] (rank={len(shape)})." - ) - shape[axis] = None - else: - shape = [None] - return {"output": Tensor(inp.dtype, tuple(shape))} - op_type = OpType("Compress", "", 11) attrs: Attributes diff --git a/tests/type_inference/test_tree_ensemble_classifier.py b/tests/type_inference/test_tree_ensemble_classifier.py index b23a60dd..25524abc 100644 --- a/tests/type_inference/test_tree_ensemble_classifier.py +++ b/tests/type_inference/test_tree_ensemble_classifier.py @@ -1,4 +1,4 @@ -# Copyright (c) QuantCo 2023-2024 +# Copyright (c) QuantCo 2023-2025 # SPDX-License-Identifier: BSD-3-Clause import numpy as np @@ -11,7 +11,7 @@ def test_tree_ensemble_regressor_inference_str(): (x,) = arguments(x=Tensor(np.float32, ("N", 5))) y, z = op_ml.tree_ensemble_classifier( - x, classlabels_strings=["a", "b"], class_ids=[1, 2, 3] + x, classlabels_strings=["a", "b", "c"], class_ids=[1, 2, 3] ) assert y.type == Tensor(np.str_, ("N",)) assert z.type == Tensor(np.float32, ("N", 3)) @@ -20,7 +20,7 @@ def test_tree_ensemble_regressor_inference_str(): def test_tree_ensemble_regressor_inference_int(): (x,) = arguments(x=Tensor(np.float32, ("N", 5))) y, z = op_ml.tree_ensemble_classifier( - x, classlabels_int64s=[1, 2], class_ids=[1, 2, 3] + x, classlabels_int64s=[1, 2, 3], class_ids=[1, 2, 3] ) assert y.type == Tensor(np.int64, ("N",)) assert z.type == Tensor(np.float32, ("N", 3)) diff --git a/tools/generate_opset.py b/tools/generate_opset.py index 9c542995..a0df15ad 100644 --- a/tools/generate_opset.py +++ b/tools/generate_opset.py @@ -1,4 +1,4 @@ -# Copyright (c) QuantCo 2023-2024 +# Copyright (c) QuantCo 2023-2025 # SPDX-License-Identifier: BSD-3-Clause from __future__ import annotations @@ -668,7 +668,7 @@ def main( "ai.onnx", 17, extras=["const"], - type_inference={"Compress": "compress11", "Loop": "loop16-fix"}, + type_inference={"Loop": "loop16-fix"}, value_propagation={"Constant": "constant13"}, out_variadic_solutions=V16_OUT_VARIADIC_SOLUTIONS, subgraphs_solutions=V16_SUBGRAPH_SOLUTIONS, @@ -680,7 +680,7 @@ def main( "ai.onnx", 18, extras=["const"], - type_inference={"Compress": "compress11"}, + type_inference={}, value_propagation={"Constant": "constant13"}, out_variadic_solutions=V18_OUT_VARIADIC_SOLUTIONS, subgraphs_solutions=V16_SUBGRAPH_SOLUTIONS, @@ -692,7 +692,7 @@ def main( "ai.onnx", 19, extras=["const"], - type_inference={"Compress": "compress11", "Loop": "loop16-fix"}, + type_inference={"Loop": "loop16-fix"}, value_propagation={"Constant": "constant13"}, out_variadic_solutions=V18_OUT_VARIADIC_SOLUTIONS, subgraphs_solutions=V16_SUBGRAPH_SOLUTIONS, @@ -715,7 +715,7 @@ def main( "ai.onnx", 21, extras=["const"], - type_inference={"Compress": "compress11", "Loop": "loop16-fix"}, + type_inference={"Loop": "loop16-fix"}, value_propagation={"Constant": "constant13"}, out_variadic_solutions=V18_OUT_VARIADIC_SOLUTIONS, subgraphs_solutions=V16_SUBGRAPH_SOLUTIONS, @@ -729,12 +729,10 @@ def main( attr_type_overrides=[(None, "dtype", ("npt.DTypeLike", "AttrDtype"))], type_inference={ "ArrayFeatureExtractor": "arrayfeatureextractor1", - "Binarizer": "binarizer1", "CategoryMapper": "categorymapper1", "Imputer": "imputer1", "LinearRegressor": "linearregressor1", "Normalizer": "normalizer1", - "OneHotEncoder": "onehotencoder1", "Scaler": "scaler1", "TreeEnsembleClassifier": "treeensembleclassifier3", "TreeEnsembleRegressor": "treeensembleregressor3", @@ -746,7 +744,6 @@ def main( 4, attr_type_overrides=[(None, "dtype", ("npt.DTypeLike", "AttrDtype"))], type_inference={ - "Binarizer": "binarizer1", "Imputer": "imputer1", "LinearRegressor": "linearregressor1", "Normalizer": "normalizer1", @@ -760,7 +757,6 @@ def main( 5, attr_type_overrides=[(None, "dtype", ("npt.DTypeLike", "AttrDtype"))], type_inference={ - "Binarizer": "binarizer1", "Imputer": "imputer1", "LinearRegressor": "linearregressor1", "Normalizer": "normalizer1", diff --git a/tools/templates/extras/promote.jinja2 b/tools/templates/extras/promote.jinja2 deleted file mode 100644 index 00528711..00000000 --- a/tools/templates/extras/promote.jinja2 +++ /dev/null @@ -1,25 +0,0 @@ -def promote( - *types: Var | np.generic | int | float | None -) -> tuple[Optional[Var], ...]: - """ - Apply constant promotion and type promotion to given parameters, creating constants and/or casting. - - None-valued parameters are only kept in for ordering. - """ - from spox._var import result_type - - promotable = [typ for typ in types if typ is not None] - if not promotable: - return typing_cast(Tuple[None, ...], types) - - target_type = result_type(*promotable) - - def _promote_target(obj: Var | np.generic | int | float | None) -> Optional[Var]: - if isinstance(obj, (np.generic, int, float)): - return const(np.array(obj, dtype=target_type)) - elif isinstance(obj, Var): - return cast(obj, to=target_type) - assert obj is None - return obj - - return tuple(typ for typ in map(_promote_target, types)) diff --git a/tools/templates/extras/xif.jinja2 b/tools/templates/extras/xif.jinja2 deleted file mode 100644 index e017a515..00000000 --- a/tools/templates/extras/xif.jinja2 +++ /dev/null @@ -1,25 +0,0 @@ -def xif( - cond: Var, /, *, - else_branch: Iterable[Var], then_branch: Iterable[Var], - to_outer_scope: bool = False -) -> Sequence[Var]: - """ - Simplified constructor for the If operator. - - The return values are equal to those of ``else_branch`` or ``then_branch``, depending on the value of the boolean ``cond`` at runtime. - - Results for both branches should have the same number of elements and their types should be respectively compatible. - - Note that elements of ``else_branch`` and ``then_branch`` may use values from the outer scope, - but to make sure they defined in the outer scope (instead of inlined in the inner scope) they - should be marked with ``intro(*outer, loop(...))``. - - For the details of If behaviour, see the docstring for if_. - """ - if to_outer_scope: - cond = intro(*else_branch, *then_branch, cond) - return if_( - cond, - else_branch=subgraph((), lambda: else_branch), - then_branch=subgraph((), lambda: then_branch) - ) diff --git a/tools/templates/extras/xloop.jinja2 b/tools/templates/extras/xloop.jinja2 deleted file mode 100644 index 87e13e60..00000000 --- a/tools/templates/extras/xloop.jinja2 +++ /dev/null @@ -1,33 +0,0 @@ -def xloop( - max_iter: Optional[Var] = None, - start_loop: Optional[Var] = None, - initial: Sequence[Var] = (), - *, - fun: Callable[..., Iterable[Var]], -) -> Sequence[Var]: - """ - Simplified constructor for the Loop operator. - - Instead of a Graph this constructor takes ``fun``, which should - take 2+N Var arguments and 1+N+K Var results. - - - The first two arguments are 1-vectors for (int64) iteration and (boolean) stop condition. - - The first result is a 1-vector (boolean) for the next stop condition. - - N: *carried* values. Carried from previous iteration (or initial). The last carried outputs are stored in the first element of the returned tuple. - - K: *scanned* values. All such outputs must be Tensors, and they are concatenated. They are stored in the second element of the returned tuple. - - Note that ``fun`` may use values from the outer scope, - but to make sure they defined in the outer scope (instead of inlined in the inner scope) they - should be marked with ``intro(*outer, loop(...))``. - - For the details of Loop behaviour, see the docstring for loop. - """ - if max_iter is not None: - max_iter = reshape(max_iter, const([1])) - if start_loop is not None: - start_loop = reshape(start_loop, const([1])) - return loop(max_iter, start_loop, initial, body=subgraph( - typing_cast(List[Type], [Tensor(np.int64, (1,)), Tensor(np.bool_, (1,))]) - + [var.unwrap_type() for var in initial], - fun - )) diff --git a/tools/templates/type_inference/binarizer1.jinja2 b/tools/templates/type_inference/binarizer1.jinja2 deleted file mode 100644 index 8d1e2d23..00000000 --- a/tools/templates/type_inference/binarizer1.jinja2 +++ /dev/null @@ -1 +0,0 @@ -return {"Y": self.inputs.X.type} if self.inputs.X.type is not None else {} \ No newline at end of file diff --git a/tools/templates/type_inference/compress11.jinja2 b/tools/templates/type_inference/compress11.jinja2 deleted file mode 100644 index a2f9b24f..00000000 --- a/tools/templates/type_inference/compress11.jinja2 +++ /dev/null @@ -1,17 +0,0 @@ -self.infer_output_types_onnx(input_prop_values) -inp, cond = self.inputs.input.unwrap_tensor(), self.inputs.condition.unwrap_tensor() -if not inp.shape: - return {'output': Tensor(inp.dtype, None)} -if cond.dtype != np.dtype(bool): - raise InferenceError("Compress input 'condition' must be a boolean dtype.") -if cond.shape and len(cond.shape) != 1: - raise InferenceError("Compress input 'condition' must be a vector (of rank 1).") -if self.attrs.axis is not None: - shape = list(inp.shape) - axis = self.attrs.axis.value - if not (-len(shape) <= axis < len(shape)): - raise InferenceError(f"Compress attribute 'axis' must in range [-rank, rank-1] (rank={len(shape)}).") - shape[axis] = None -else: - shape = [None] -return {'output': Tensor(inp.dtype, tuple(shape))} diff --git a/tools/templates/type_inference/if16.jinja2 b/tools/templates/type_inference/if16.jinja2 deleted file mode 100644 index 84c55635..00000000 --- a/tools/templates/type_inference/if16.jinja2 +++ /dev/null @@ -1,16 +0,0 @@ -else_body = self.attrs.else_branch.value -then_body = self.attrs.then_branch.value -assert not else_body.arguments -assert not then_body.arguments -else_map = else_body.results -then_map = then_body.results -assert len(else_map) == len(then_map) -result = {} -for name, first, second in zip(self.outputs.as_dict(), else_map.values(), then_map.values()): - if not (first.type <= second.type or second.type <= first.type): - raise TypeError( - f"Else/then branches must have compatible result types, " - f"got {first.type} <> {second.type}." - ) - result[name] = first.type & second.type -return result \ No newline at end of file diff --git a/tools/templates/type_inference/loop16.jinja2 b/tools/templates/type_inference/loop16.jinja2 deleted file mode 100644 index ceec0bcc..00000000 --- a/tools/templates/type_inference/loop16.jinja2 +++ /dev/null @@ -1,49 +0,0 @@ -if self.inputs.M.type is not None: - assert self.inputs.M.unwrap_type() <= Tensor(np.int64, (1,)), f"Maximum trip-count must be an int64 1-vector, got {self.inputs.M.type}" -if self.inputs.cond.type is not None: - assert self.inputs.cond.unwrap_type() <= Tensor(np.bool_, (1,)), f"Terminal condition must be a boolean 1-vector, got {self.inputs.cond.type}" - -carried_init = self.inputs.v_initial - -body = self.attrs.body.value -n = len(body.arguments) - 2 -assert n >= 0, f"Too few Loop body arguments: {len(body.arguments)}" -assert n == len(carried_init), f"Mismatched initial carry/input carry: {n} != {len(carried_init)}" -k = len(body.results) - n - 1 -assert k >= 0, f"Too few Loop body results ({len(body.arguments)} arguments): {len(body.results)}" - -iter_num, cond, *carried_in = list(body.arguments.values()) -results = list(body.results.values()) -next_cond, carried_out, scan_outputs = results[0], results[1:n+1], results[n+1:] - -body_signature = ( - f"init(max: {self.inputs.M.type}, cond: {self.inputs.cond.type}, " - f"carried: {[a.type for a in carried_init]}) ~> " - f"in(iter: {iter_num.type}, cond: {cond.type}, " - f"carried: {[a.type for a in carried_in]}) => " - f"out(cond: {next_cond.type}, carried: {[a.type for a in carried_out]}, " - f"scan: {[a.type for a in scan_outputs]})" -) - -assert iter_num.unwrap_type() <= Tensor(np.int64, (1,)) -assert cond.unwrap_type() <= Tensor(np.bool_, (1,)) -assert len(carried_init) == len(carried_in) == len(carried_out), f"Carried lengths (n) must match, body: {body_signature}" -assert all(type_match(x.type, y.type) and type_match(y.type, z.type) and type_match(x.type, z.type) for x, y, z in zip(carried_init, carried_in, carried_out)), \ - f"Types for carried Loop values must all match, body: {body_signature}" -carried_types = [x.type & y.type & z.type for x, y, z in zip(carried_init, carried_in, carried_out)] -assert all(isinstance(s.type, Tensor) for s in scan_outputs), \ - f"Loop scanned outputs must be Tensors, as they get concatenated, body: {body_signature}" - -output_types: Dict[str, Type] = {} -output_names = list(self.outputs.as_dict().keys()) -carried_names, scan_names = output_names[:n], output_names[n:] - -for name, typ in zip(carried_names, carried_types): - output_types[name] = typ -for name, var in zip(scan_names, scan_outputs): - tensor = var.unwrap_tensor() - assert tensor.shape is not None - scan_shape = (None,) + tensor.shape # type: ignore - output_types[name] = Tensor(tensor.dtype, scan_shape) - -return output_types diff --git a/tools/templates/type_inference/onehot11.jinja2 b/tools/templates/type_inference/onehot11.jinja2 deleted file mode 100644 index 7fd911c0..00000000 --- a/tools/templates/type_inference/onehot11.jinja2 +++ /dev/null @@ -1,21 +0,0 @@ -self.infer_output_types_onnx() -if not (self.inputs.indices.type and self.inputs.depth.type and self.inputs.values.type): - return {} -indices = self.inputs.indices.unwrap_tensor() -depth = self.inputs.depth.unwrap_tensor() -values = self.inputs.values.unwrap_tensor() -if depth.shape is not None and len(depth.shape) != 0: - raise InferenceError("Number of classes must be a scalar.") -if values.shape is not None and len(values.shape) != 1: - raise InferenceError("Number of values must be a vector (of length 2).") -if indices.shape is not None: - axis = self.attrs.axis.value - if not (-len(indices.shape) - 1 <= axis <= len(indices.shape)): - raise InferenceError(f"Attribute axis={axis} out of range [-r-1, r] for indices rank r={len(indices.shape)}.") - if axis < 0: - # + 1 because slices on negatives are still right-open - axis += len(indices.shape) + 1 - shape = indices.shape[:axis] + (None,) + indices.shape[axis:] -else: - shape = None -return {'output': Tensor(values.dtype, shape)} \ No newline at end of file diff --git a/tools/templates/type_inference/onehotencoder1.jinja2 b/tools/templates/type_inference/onehotencoder1.jinja2 deleted file mode 100644 index afc78d7c..00000000 --- a/tools/templates/type_inference/onehotencoder1.jinja2 +++ /dev/null @@ -1,14 +0,0 @@ -if not self.inputs.fully_typed: - return {} -if self.attrs.cats_int64s: - n_encodings = len(self.attrs.cats_int64s.value) -elif self.attrs.cats_strings: - n_encodings = len(self.attrs.cats_strings.value) -else: - raise InferenceError( - "Either `cats_int64s` or `cats_strings` attributes must be set." - ) -shape = (*self.inputs.X.unwrap_tensor().shape, n_encodings) # type: ignore -return { - "Y": Tensor(dtype=np.float32, shape=shape) -}