163 lines
4.9 KiB
Python
163 lines
4.9 KiB
Python
# Copyright 2020 The HuggingFace Team. All rights reserved.
|
|
#
|
|
# Licensed 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.
|
|
|
|
import copy
|
|
import re
|
|
|
|
|
|
class TrialShortNamer:
|
|
PREFIX = "hp"
|
|
DEFAULTS = {}
|
|
NAMING_INFO = None
|
|
|
|
@classmethod
|
|
def set_defaults(cls, prefix, defaults):
|
|
cls.PREFIX = prefix
|
|
cls.DEFAULTS = defaults
|
|
cls.build_naming_info()
|
|
|
|
@staticmethod
|
|
def shortname_for_word(info, word):
|
|
if len(word) == 0:
|
|
return ""
|
|
short_word = None
|
|
if any(char.isdigit() for char in word):
|
|
raise Exception(f"Parameters should not contain numbers: '{word}' contains a number")
|
|
if word in info["short_word"]:
|
|
return info["short_word"][word]
|
|
for prefix_len in range(1, len(word) + 1):
|
|
prefix = word[:prefix_len]
|
|
if prefix in info["reverse_short_word"]:
|
|
continue
|
|
else:
|
|
short_word = prefix
|
|
break
|
|
|
|
if short_word is None:
|
|
# Paranoid fallback
|
|
def int_to_alphabetic(integer):
|
|
s = ""
|
|
while integer != 0:
|
|
s = chr(ord("A") + integer % 10) + s
|
|
integer //= 10
|
|
return s
|
|
|
|
i = 0
|
|
while True:
|
|
sword = word + "#" + int_to_alphabetic(i)
|
|
if sword in info["reverse_short_word"]:
|
|
continue
|
|
else:
|
|
short_word = sword
|
|
break
|
|
|
|
info["short_word"][word] = short_word
|
|
info["reverse_short_word"][short_word] = word
|
|
return short_word
|
|
|
|
@staticmethod
|
|
def shortname_for_key(info, param_name):
|
|
words = param_name.split("_")
|
|
|
|
shortname_parts = [TrialShortNamer.shortname_for_word(info, word) for word in words]
|
|
|
|
# We try to create a separatorless short name, but if there is a collision we have to fallback
|
|
# to a separated short name
|
|
separators = ["", "_"]
|
|
|
|
for separator in separators:
|
|
shortname = separator.join(shortname_parts)
|
|
if shortname not in info["reverse_short_param"]:
|
|
info["short_param"][param_name] = shortname
|
|
info["reverse_short_param"][shortname] = param_name
|
|
return shortname
|
|
|
|
return param_name
|
|
|
|
@staticmethod
|
|
def add_new_param_name(info, param_name):
|
|
short_name = TrialShortNamer.shortname_for_key(info, param_name)
|
|
info["short_param"][param_name] = short_name
|
|
info["reverse_short_param"][short_name] = param_name
|
|
|
|
@classmethod
|
|
def build_naming_info(cls):
|
|
if cls.NAMING_INFO is not None:
|
|
return
|
|
|
|
info = {
|
|
"short_word": {},
|
|
"reverse_short_word": {},
|
|
"short_param": {},
|
|
"reverse_short_param": {},
|
|
}
|
|
|
|
field_keys = list(cls.DEFAULTS.keys())
|
|
|
|
for k in field_keys:
|
|
cls.add_new_param_name(info, k)
|
|
|
|
cls.NAMING_INFO = info
|
|
|
|
@classmethod
|
|
def shortname(cls, params):
|
|
cls.build_naming_info()
|
|
assert cls.PREFIX is not None
|
|
name = [copy.copy(cls.PREFIX)]
|
|
|
|
for k, v in params.items():
|
|
if k not in cls.DEFAULTS:
|
|
raise Exception(f"You should provide a default value for the param name {k} with value {v}")
|
|
if v == cls.DEFAULTS[k]:
|
|
# The default value is not added to the name
|
|
continue
|
|
|
|
key = cls.NAMING_INFO["short_param"][k]
|
|
|
|
if isinstance(v, bool):
|
|
v = 1 if v else 0
|
|
|
|
sep = "" if isinstance(v, (int, float)) else "-"
|
|
e = f"{key}{sep}{v}"
|
|
name.append(e)
|
|
|
|
return "_".join(name)
|
|
|
|
@classmethod
|
|
def parse_repr(cls, repr):
|
|
repr = repr[len(cls.PREFIX) + 1 :]
|
|
if repr == "":
|
|
values = []
|
|
else:
|
|
values = repr.split("_")
|
|
|
|
parameters = {}
|
|
|
|
for value in values:
|
|
if "-" in value:
|
|
p_k, p_v = value.split("-")
|
|
else:
|
|
p_k = re.sub("[0-9.]", "", value)
|
|
p_v = float(re.sub("[^0-9.]", "", value))
|
|
|
|
key = cls.NAMING_INFO["reverse_short_param"][p_k]
|
|
|
|
parameters[key] = p_v
|
|
|
|
for k in cls.DEFAULTS:
|
|
if k not in parameters:
|
|
parameters[k] = cls.DEFAULTS[k]
|
|
|
|
return parameters
|