• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2022 Huawei Technologies Co., Ltd
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14# ============================================================================
15"""Unique name producer for target, name of node, class name, etc."""
16
17from typing import Union, Tuple
18
19from ..node import Node
20from ..api.node_type import NodeType
21
22
23class Namer:
24    """
25    Used for unique identity in a class-scope. current used for target of construct-function.
26    Namer records times of name been used, and add prefix to origin name for unique name. For example, when a Namer
27    record "name1" has been used 10 times, when a new request require a unique name base on 'name1', namer will respond
28    "name1_10" as unique name.
29    """
30
31    def __init__(self):
32        """Constructor of Namer."""
33        self._names: {str: int} = {}
34
35    @staticmethod
36    def _real_name(name: str) -> Tuple[str, int]:
37        """
38        Find real name. For example, "name1" is the real name of "name1_10", "name1" is the real name of "name1_10_3".
39        If not find real name before find unique name, unique name may be not unique. For example:
40
41            1. "name1" has been used 10 times, which means "name1", "name1_2", "name1_3" ... "name1_10" has been used;
42            2. new request require a unique name base on 'name1_5'
43            3. If namer not find real name of "name1_5", namer will find that "name1_5" is never used and respond
44            "name1_5" as unique name which is used before, actually.
45
46        Args:
47            name (str): Origin name which may have digit prefix.
48
49        Returns:
50            A string represents real-name and a int represents suffix.
51        """
52        if name == '_':
53            return name, None
54        pos = name.rfind("_")
55        if pos == -1 or pos == len(name) - 1:
56            return name, None
57        digit = True
58        for i in range(pos + 1, len(name)):
59            if not name[i].isdigit():
60                digit = False
61                break
62        if digit:
63            return name[:pos], int(name[pos + 1:])
64        return name, None
65
66    def get_name(self, origin_name: str) -> str:
67        """
68        Get unique name from 'origin_name'.
69
70        Args:
71            origin_name (str): Origin name which may be duplicated.
72
73        Returns:
74            A string represents unique-name.
75        """
76        if origin_name == '_':
77            return origin_name
78        real_name, suffix_idx = Namer._real_name(origin_name)
79        name = origin_name
80        number = self._names.get(name)
81        if number is None:
82            self._names[name] = 1
83            if not suffix_idx:
84                # When _names is {x:2} and origin_name is y,
85                # origin_name is not in _names and can be returned.
86                return name
87            if suffix_idx and not self._names.get(real_name, -1) > suffix_idx:
88                # When _names is {x:2} and origin_name is x_3,
89                # return x_3 and update _names to {x:2, x_3:1}
90                return name
91            # When _names is {x:2} and origin_name is x_1,
92            # set new_name to x_1_1 by set number to 1, and continue to update name.
93            number = 1
94        while True:
95            new_name = f"{name}_{number}"
96            number += 1
97            self._names[name] = number
98            # When _names is {x:2, x_3:1}, origin_name is x and number is update to 3,
99            # new_name x_3 is conflict with key x_3, so this new_name need to be skipped.
100            if new_name in self._names.keys():
101                continue
102            return new_name
103
104    def add_name(self, name: str):
105        """
106        Add a name to Namer which should be unique.
107
108        Args:
109            name (str): A name should be unique in current namer.
110        """
111        if self._names.get(name) is None:
112            self._names[name] = 1
113
114
115class TargetNamer(Namer):
116    """
117    Used for unique-ing targets of node.
118    """
119    def get_unique_name(self, origin_name: str) -> str:
120        """
121        Get unique name from 'origin_name'.
122
123        Args:
124            origin_name (str): Origin name which may be duplicated.
125
126        Returns:
127            A string represents unique-name.
128        """
129        return super(TargetNamer, self).get_name(origin_name)
130
131
132class NodeNamer(Namer):
133    """
134    Used for unique-ing node-name which is also used as field of init-function and key of global_vars
135    """
136
137    def get_name(self, node_or_name: Union[Node, str]) -> str:
138        """
139        Override get_name in Namer class.
140        Get unique node_name from 'origin_name' or an instance of node.
141
142        Args:
143            node_or_name (Union[Node, str]): A string represents candidate node_name or an instance of node who require
144                                             A unique node_name.
145
146        Returns:
147            A string represents unique node_name.
148        """
149        if isinstance(node_or_name, Node):
150            origin_name = node_or_name.get_name()
151            if origin_name is None or not origin_name:
152                if node_or_name.get_node_type() in (NodeType.CallCell, NodeType.CallPrimitive, NodeType.CallFunction,
153                                                    NodeType.Tree):
154                    origin_name = type(node_or_name.get_instance()).__name__
155                elif node_or_name.get_node_type() == NodeType.Python:
156                    if node_or_name.get_instance():
157                        origin_name = type(node_or_name.get_instance()).__name__
158                    else:
159                        origin_name = "python_node"
160                elif node_or_name.get_node_type() == NodeType.Input:
161                    origin_name = "parameter"
162                elif node_or_name.get_node_type() == NodeType.Output:
163                    origin_name = "return"
164                elif node_or_name.get_node_type() == NodeType.MathOps:
165                    origin_name = "math_ops"
166                else:
167                    origin_name = str(node_or_name.get_node_type())
168        elif isinstance(node_or_name, str):
169            if not node_or_name:
170                raise ValueError("input node_name is empty.")
171            origin_name = node_or_name
172        else:
173            raise ValueError("unexpected type of node_or_name:", type(node_or_name))
174        return super(NodeNamer, self).get_name(origin_name)
175
176
177class ClassNamer(Namer):
178    """
179    Used for unique-ing class name in a network.
180
181    Class name should be unique in a network, in other word, in a Rewrite process. So please do not invoke constructor
182    of `ClassNamer` and call `instance()` of `ClassNamer` to obtain singleton of ClassNamer.
183    """
184
185    def __init__(self):
186        super().__init__()
187        self._prefix = "Opt"
188
189    @classmethod
190    def instance(cls):
191        """
192        Class method of `ClassNamer` for singleton of `ClassNamer`.
193
194        Returns:
195            An instance of `ClassNamer` as singleton of `ClassNamer`.
196        """
197
198        if not hasattr(ClassNamer, "_instance"):
199            ClassNamer._instance = ClassNamer()
200        return ClassNamer._instance
201
202    def get_name(self, origin_name: str) -> str:
203        """
204        Unique input `origin_name`.
205
206        Args:
207            origin_name (str): A string represents original class name.
208
209        Returns:
210            A string represents a unique class name generated from `origin_name`.
211        """
212
213        return super(ClassNamer, self).get_name(origin_name + self._prefix)
214
215    def add_name(self, name: str):
216        """
217        Declare a `name` so that other class can not apply this `name` anymore.
218
219        Args:
220            name (str): A string represents a class name.
221        """
222
223        super(ClassNamer, self).add_name(name + self._prefix)
224
225class FunctionNamer(Namer):
226    """
227    Used for unique-ing function name in a network.
228
229    Function name should be unique in a network, in other word, in a Rewrite process. So please do not invoke
230    constructor of `FunctionNamer` and call `instance()` of `FunctionNamer` to obtain singleton of FunctionNamer.
231    """
232
233    def __init__(self):
234        super().__init__()
235        self._prefix = ""
236
237    @classmethod
238    def instance(cls):
239        """
240        Class method of `FunctionNamer` for singleton of `FunctionNamer`.
241
242        Returns:
243            An instance of `FunctionNamer` as singleton of `FunctionNamer`.
244        """
245
246        if not hasattr(FunctionNamer, "_instance"):
247            FunctionNamer._instance = FunctionNamer()
248        return FunctionNamer._instance
249
250    def get_name(self, origin_name: str) -> str:
251        """
252        Unique input `origin_name`.
253
254        Args:
255            origin_name (str): A string represents original function name.
256
257        Returns:
258            A string represents a unique function name generated from `origin_name`.
259        """
260
261        return super(FunctionNamer, self).get_name(origin_name + self._prefix)
262
263    def add_name(self, name: str):
264        """
265        Declare a `name` so that other function can not apply this `name` anymore.
266
267        Args:
268            name (str): A string represents a function name.
269        """
270
271        super(FunctionNamer, self).add_name(name + self._prefix)
272