# Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. # pylint: disable=invalid-name # ruff: noqa: E731 """Default legalization function for creation operators.""" import numpy as np import tvm from tvm import te, tirx, topi from tvm.ir import Call from ...block_builder import BlockBuilder from ...expr import Expr, ShapeExpr, const from ...type import ShapeType from .common import LegalizeFunc, _try_convert_to_scalar_const, register_legalize def _full(is_like: bool, fill_value: float | None, primfunc_name: str) -> LegalizeFunc: def full_call_te(bb: BlockBuilder, call: Call) -> Expr: _fill_value = ( _try_convert_to_scalar_const(call.args[1], python_native=True) if fill_value is None else fill_value ) shape = call.args[0].ty.shape if is_like else call.args[0] if isinstance(shape, ShapeExpr): output_shape = shape.values else: assert isinstance(shape.ty, ShapeType) assert shape.ty.ndim >= 0 shape = bb.emit(shape) output_shape = [tirx.Var(f"s{i}", "int64") for i in range(shape.ty.ndim)] bb.match_cast(shape, ShapeType(output_shape)) return bb.call_te( topi.full, output_shape, call.ty.dtype, _fill_value, primfunc_name_hint=primfunc_name, ) return full_call_te def _tril_triu(is_upper: bool, primfunc_name: str) -> LegalizeFunc: def tril_triu_call_te(bb: BlockBuilder, call: Call) -> Expr: data, k = call.args return bb.call_te( topi.trilu, data, k, upper=is_upper, primfunc_name_hint=primfunc_name, ) return tril_triu_call_te register_legalize("relax.full", _full(is_like=False, fill_value=None, primfunc_name="full")) register_legalize("relax.full_like", _full(is_like=True, fill_value=None, primfunc_name="full")) register_legalize("relax.ones", _full(is_like=False, fill_value=1.0, primfunc_name="ones")) register_legalize("relax.ones_like", _full(is_like=True, fill_value=1.0, primfunc_name="ones")) register_legalize("relax.zeros", _full(is_like=False, fill_value=0.0, primfunc_name="zeros")) register_legalize("relax.zeros_like", _full(is_like=True, fill_value=0.0, primfunc_name="zeros")) register_legalize("relax.tril", _tril_triu(is_upper=False, primfunc_name="tril")) register_legalize("relax.triu", _tril_triu(is_upper=True, primfunc_name="triu")) def _eye(is_like: bool, primfunc_name: str) -> LegalizeFunc: def eye_call_te(bb: BlockBuilder, call: Call) -> Expr: _convert_to_scalar_const = lambda x: _try_convert_to_scalar_const(x, python_native=True) if is_like: x = call.args[0] k = _convert_to_scalar_const(call.args[1]) if len(call.args) > 1 else 0 n, m = x.ty.shape dtype = x.ty.dtype else: n = _convert_to_scalar_const(call.args[0]) m = _convert_to_scalar_const(call.args[1]) if len(call.args) > 1 else n k = _convert_to_scalar_const(call.args[2]) if len(call.args) > 2 else 0 dtype = call.attrs.dtype return bb.call_te( topi.eye, n, m, k, dtype, primfunc_name_hint=primfunc_name, ) return eye_call_te register_legalize("relax.eye", _eye(is_like=False, primfunc_name="eye")) register_legalize("relax.eye_like", _eye(is_like=True, primfunc_name="eye_like")) @register_legalize("relax.arange") def _arange(bb: BlockBuilder, call: Call) -> Expr: assert len(call.args) == 3 assert all(tvm.ir.is_prim_expr(x) for x in call.args) start, end, step = call.args dtype = call.attrs.dtype def is_const_scalar(x: tirx.Expr): return isinstance(x, tirx.IntImm | tirx.FloatImm) if all([is_const_scalar(x) for x in call.args]): return const(np.arange(start.value, end.value, step.value, dtype=dtype), dtype=dtype) else: return bb.call_te(topi.arange, start, end, step, dtype) @register_legalize("relax.shape_to_tensor") def _shape_to_tensor(bb: BlockBuilder, call: Call) -> Expr: shape = call.args[0] values = shape.values if isinstance(shape, ShapeExpr) else shape.ty.values if values is None: return call values = list(values) n = len(values) symbolic = [v for v in values if not isinstance(v, tirx.IntImm)] def te_shape_to_tensor(*sym): sym = list(sym) resolved = [v if isinstance(v, tirx.IntImm) else sym.pop(0) for v in values] def fcompute(i): result = tirx.const(0, "int64") for idx in range(n - 1, -1, -1): result = tirx.if_then_else(i == idx, tirx.Cast("int64", resolved[idx]), result) return result return te.compute((n,), fcompute, name="shape_to_tensor") return bb.call_te(te_shape_to_tensor, *symbolic, primfunc_name_hint="shape_to_tensor") @register_legalize("relax.hamming_window") def _hamming_window(bb: BlockBuilder, call: Call) -> Expr: assert len(call.args) == 4 dtype = call.attrs.dtype window_size = call.args[0] periodic = call.args[1] alpha = call.args[2] beta = call.args[3] return bb.call_te(topi.hamming_window, window_size, periodic, alpha, beta, dtype)