Files
wehub-resource-sync 26446540fa
Lint / lint (push) Has been cancelled
CI / MacOS (push) Has been cancelled
CI / Windows (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:36:25 +08:00

322 lines
10 KiB
Python

# 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.
"""Pattern table for NNAPI backend"""
from collections.abc import Mapping
from tvm.ir import IRModule
from tvm.relax.dpl.pattern import (
DFPattern,
is_op,
wildcard,
)
from tvm.relax.transform import FuseOpsByPattern, MergeCompositeFunctions
from ..pattern_registry import get_patterns_with_prefix, register_patterns
def elementwise_binary_patterns() -> list[tuple[str, DFPattern, Mapping[str, DFPattern]]]:
"""
Returns a list of tuples representing elementwise binary operation patterns mapped
between NNAPI and Relax frameworks.
"""
def _elementwise_binary_pattern(
pattern_name: str,
op_name: str,
) -> tuple[str, DFPattern, Mapping[str, DFPattern]]:
input0 = wildcard()
input1 = wildcard()
pattern = is_op(op_name)(input0, input1)
return (pattern_name, pattern, {})
return [
_elementwise_binary_pattern("nnapi.add", "relax.add"),
_elementwise_binary_pattern("nnapi.mul", "relax.multiply"),
_elementwise_binary_pattern("nnapi.div", "relax.divide"),
_elementwise_binary_pattern("nnapi.sub", "relax.subtract"),
_elementwise_binary_pattern("nnapi.pow", "relax.power"),
_elementwise_binary_pattern("nnapi.equal", "relax.equal"),
_elementwise_binary_pattern("nnapi.greater", "relax.greater"),
_elementwise_binary_pattern("nnapi.greater_equal", "relax.greater_equal"),
_elementwise_binary_pattern("nnapi.less", "relax.less"),
_elementwise_binary_pattern("nnapi.less_equal", "relax.less_equal"),
_elementwise_binary_pattern("nnapi.not_equal", "relax.not_equal"),
_elementwise_binary_pattern("nnapi.maximum", "relax.maximum"),
_elementwise_binary_pattern("nnapi.minimum", "relax.minimum"),
]
def unary_patterns() -> list[tuple[str, DFPattern, Mapping[str, DFPattern]]]:
"""
Returns a list of tuples representing unary operation patterns mapped
between NNAPI and Relax frameworks.
"""
def _unary_pattern(
pattern_name: str, op_name: str
) -> tuple[str, DFPattern, Mapping[str, DFPattern]]:
input0 = wildcard()
pattern = is_op(op_name)(input0)
return (pattern_name, pattern, {})
return [
_unary_pattern("nnapi.floor", "relax.floor"),
_unary_pattern("nnapi.relu", "relax.nn.relu"),
_unary_pattern("nnapi.logistic", "relax.sigmoid"),
_unary_pattern("nnapi.softmax", "relax.nn.softmax"),
_unary_pattern("nnapi.tanh", "relax.tanh"),
_unary_pattern("nnapi.abs", "relax.abs"),
_unary_pattern("nnapi.exp", "relax.exp"),
_unary_pattern("nnapi.log", "relax.log"),
_unary_pattern("nnapi.neg", "relax.negative"),
_unary_pattern("nnapi.cast", "relax.astype"),
_unary_pattern("nnapi.sqrt", "relax.sqrt"),
_unary_pattern("nnapi.rsqrt", "relax.rsqrt"),
]
def matmul_pattern() -> tuple[str, DFPattern, Mapping[str, DFPattern]]:
"""
Returns a tuple representing matmul operation patterns mapped
between NNAPI and Relax frameworks.
"""
input0 = wildcard()
input1 = wildcard()
pattern = is_op("relax.matmul")(input0, input1)
return ("nnapi.batch_matmul", pattern, {})
def permute_dims_pattern() -> tuple[str, DFPattern, Mapping[str, DFPattern]]:
"""
Returns a tuple representing permute operation patterns mapped
between NNAPI and Relax frameworks.
"""
input0 = wildcard()
pattern = is_op("relax.permute_dims")(input0)
return ("nnapi.transpose", pattern, {})
def astype_pattern() -> tuple[str, DFPattern, Mapping[str, DFPattern]]:
"""
Returns a tuple representing astype operation patterns mapped
between NNAPI and Relax frameworks.
"""
input0 = wildcard().has_dtype("float16") | wildcard().has_dtype("float32")
pattern = is_op("relax.astype")(input0).has_dtype("float16") | is_op("relax.astype")(
input0
).has_dtype("float32")
return ("nnapi.cast", pattern, {})
def mean_pattern() -> tuple[str, DFPattern, Mapping[str, DFPattern]]:
"""
Returns a tuple representing mean operation patterns mapped
between NNAPI and Relax frameworks.
"""
input0 = wildcard()
pattern = is_op("relax.mean")(input0)
return ("nnapi.mean", pattern, {})
def conv2d_pattern() -> tuple[str, DFPattern, Mapping[str, DFPattern]]:
"""
Returns a tuple representing conv2d operation patterns mapped
between NNAPI and Relax frameworks.
"""
input0 = wildcard()
input1 = wildcard()
input2 = wildcard()
conv = is_op("relax.nn.conv2d")(input0, input1)
pattern = is_op("relax.add")(conv, input2)
return ("nnapi.conv2d", pattern, {})
def max_pool2d_pattern() -> tuple[str, DFPattern, Mapping[str, DFPattern]]:
"""
Returns a tuple representing max_pool2d operation patterns mapped
between NNAPI and Relax frameworks.
"""
input0 = wildcard()
pattern = is_op("relax.nn.max_pool2d")(input0)
return ("nnapi.max_pool_2d", pattern, {})
register_patterns(
[
*elementwise_binary_patterns(),
*unary_patterns(),
matmul_pattern(),
permute_dims_pattern(),
astype_pattern(),
mean_pattern(),
conv2d_pattern(),
max_pool2d_pattern(),
]
)
def min_feature_level(pattern_name: str) -> int:
"""
Returns the minimum feature level required to support a given NNAPI operation pattern.
Args:
pattern_name (str): The name of the NNAPI operation pattern
(e.g., "nnapi.add", "nnapi.conv2d").
Returns:
int: The minimum feature level for the specified pattern, or 1 if the pattern is not found.
"""
levels = {
"nnapi.add": 1,
"nnapi.average_pool_2d": 1,
"nnapi.concatenation": 1,
"nnapi.conv2d": 1,
"nnapi.depthwise_conv_2d": 1,
"nnapi.depth_to_space": 1,
"nnapi.dequantize": 1,
"nnapi.embedding_lookup": 1,
"nnapi.floor": 1,
"nnapi.fully_connected": 1,
"nnapi.hashtable_lookup": 1,
"nnapi.l2_normalization": 1,
"nnapi.l2_pool_2d": 1,
"nnapi.local_response_normalization": 1,
"nnapi.logistic": 1,
"nnapi.lsh_projection": 1,
"nnapi.lstm": 1,
"nnapi.max_pool_2d": 1,
"nnapi.mul": 1,
"nnapi.relu": 1,
"nnapi.relu1": 1,
"nnapi.relu6": 1,
"nnapi.reshape": 1,
"nnapi.resize_bilinear": 1,
"nnapi.rnn": 1,
"nnapi.softmax": 1,
"nnapi.space_to_depth": 1,
"nnapi.svdf": 1,
"nnapi.tanh": 1,
"nnapi.batch_to_space_nd": 2,
"nnapi.div": 2,
"nnapi.mean": 2,
"nnapi.pad": 2,
"nnapi.space_to_batch_nd": 2,
"nnapi.squeeze": 2,
"nnapi.strided_slice": 2,
"nnapi.sub": 2,
"nnapi.transpose": 2,
"nnapi.abs": 3,
"nnapi.argmax": 3,
"nnapi.argmin": 3,
"nnapi.axis_aligned_bbox_transform": 3,
"nnapi.bidirectional_sequence_lstm": 3,
"nnapi.bidirectional_sequence_rnn": 3,
"nnapi.box_with_nms_limit": 3,
"nnapi.cast": 3,
"nnapi.channel_shuffle": 3,
"nnapi.detection_postprocessing": 3,
"nnapi.equal": 3,
"nnapi.exp": 3,
"nnapi.expand_dims": 3,
"nnapi.gather": 3,
"nnapi.generate_proposals": 3,
"nnapi.greater": 3,
"nnapi.greater_equal": 3,
"nnapi.grouped_conv_2d": 3,
"nnapi.heatmap_max_keypoint": 3,
"nnapi.instance_normalization": 3,
"nnapi.less": 3,
"nnapi.less_equal": 3,
"nnapi.log": 3,
"nnapi.logical_and": 3,
"nnapi.logical_not": 3,
"nnapi.logical_or": 3,
"nnapi.log_softmax": 3,
"nnapi.maximum": 3,
"nnapi.minimum": 3,
"nnapi.neg": 3,
"nnapi.not_equal": 3,
"nnapi.pad_v2": 3,
"nnapi.pow": 3,
"nnapi.prelu": 3,
"nnapi.quantize": 3,
"nnapi.quantized_16bit_lstm": 3,
"nnapi.random_multinomial": 3,
"nnapi.reduce_all": 3,
"nnapi.reduce_any": 3,
"nnapi.reduce_max": 3,
"nnapi.reduce_min": 3,
"nnapi.reduce_prod": 3,
"nnapi.reduce_sum": 3,
"nnapi.roi_align": 3,
"nnapi.roi_pooling": 3,
"nnapi.rsqrt": 3,
"nnapi.select": 3,
"nnapi.sin": 3,
"nnapi.slice": 3,
"nnapi.split": 3,
"nnapi.sqrt": 3,
"nnapi.tile": 3,
"nnapi.topk_v2": 3,
"nnapi.transpose_conv_2d": 3,
"nnapi.unidirectional_sequence_lstm": 3,
"nnapi.unidirectional_sequence_rnn": 3,
"nnapi.resize_nearest_neighbor": 3,
"nnapi.quantized_lstm": 4,
"nnapi.if": 4,
"nnapi.while": 4,
"nnapi.elu": 4,
"nnapi.hard_swish": 4,
"nnapi.fill": 4,
"nnapi.rank": 4,
"nnapi.batch_matmul": 6,
"nnapi.pack": 6,
"nnapi.mirror_pad": 7,
"nnapi.reverse": 7,
}
return levels[pattern_name]
def partition_for_nnapi(mod: IRModule, feature_level: int | None = None) -> IRModule:
"""Partition the graph greedily offloading supported operators to NNAPI.
Parameters
----------
mod : tvm.ir.IRModule
The module to run passes on.
feature_level : Optional[int]
The maximum NNAPI feature level.
Returns
-------
mod : tvm.ir.IRModule
Annotated and partitioned module.
"""
patterns = get_patterns_with_prefix("nnapi")
if feature_level is not None:
patterns = [pat for pat in patterns if feature_level >= min_feature_level(pat.name)]
mod = FuseOpsByPattern(patterns, bind_constants=False, annotate_codegen=False)(mod)
mod = MergeCompositeFunctions()(mod)
return mod