You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tvm.apache.org by ma...@apache.org on 2023/05/19 09:49:38 UTC

[tvm] branch main updated: [AutoTVM] Use f-strings for string formatting, NFC (#14884)

This is an automated email from the ASF dual-hosted git repository.

masahi pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm.git


The following commit(s) were added to refs/heads/main by this push:
     new 61fe8a7027 [AutoTVM] Use f-strings for string formatting, NFC (#14884)
61fe8a7027 is described below

commit 61fe8a7027eeb73daf6f283407717e0c7bb6bad8
Author: Krzysztof Parzyszek <kp...@quicinc.com>
AuthorDate: Fri May 19 04:49:28 2023 -0500

    [AutoTVM] Use f-strings for string formatting, NFC (#14884)
    
    * [AutoTVM] Use f-strings for string formatting, NFC
    
    Replace uses of % and .format() with f-strings.
    
    Reformat modified files.
    
    * Fix typo: epochs -> epoch
---
 python/tvm/auto_scheduler/cost_model/xgb_model.py  | 20 +++-----
 python/tvm/auto_scheduler/dispatcher.py            |  2 +-
 python/tvm/auto_scheduler/feature.py               | 15 +++---
 python/tvm/auto_scheduler/measure.py               | 18 ++-----
 python/tvm/auto_scheduler/search_policy.py         |  2 +-
 python/tvm/auto_scheduler/search_task.py           | 23 ++++-----
 python/tvm/auto_scheduler/task_scheduler.py        | 15 ++----
 python/tvm/auto_scheduler/utils.py                 |  8 ++-
 python/tvm/auto_scheduler/workload_registry.py     |  6 +--
 python/tvm/autotvm/feature.py                      |  9 ++--
 python/tvm/autotvm/graph_tuner/base_graph_tuner.py | 10 ++--
 python/tvm/autotvm/graph_tuner/pbqp_tuner.py       |  4 +-
 .../autotvm/graph_tuner/utils/traverse_graph.py    |  8 ++-
 python/tvm/autotvm/measure/measure_methods.py      | 52 ++++---------------
 python/tvm/autotvm/record.py                       |  4 +-
 python/tvm/autotvm/task/dispatcher.py              | 14 +++--
 python/tvm/autotvm/task/space.py                   | 30 +++++------
 python/tvm/autotvm/task/task.py                    | 10 ++--
 python/tvm/autotvm/testing/tune_relay.py           | 59 ++++------------------
 python/tvm/autotvm/tophub.py                       | 10 ++--
 python/tvm/autotvm/tuner/sa_model_optimizer.py     |  2 +-
 python/tvm/autotvm/tuner/xgboost_cost_model.py     | 28 +++++-----
 22 files changed, 116 insertions(+), 233 deletions(-)

diff --git a/python/tvm/auto_scheduler/cost_model/xgb_model.py b/python/tvm/auto_scheduler/cost_model/xgb_model.py
index 328e25db7b..c7cdb15634 100644
--- a/python/tvm/auto_scheduler/cost_model/xgb_model.py
+++ b/python/tvm/auto_scheduler/cost_model/xgb_model.py
@@ -210,10 +210,7 @@ class XGBModel(PythonBasedModel):
                 CustomCallback(
                     stopping_rounds=50,
                     metric="tr-p-rmse",
-                    fevals=[
-                        pack_sum_rmse,
-                        pack_sum_average_peak_score(self.plan_size),
-                    ],
+                    fevals=[pack_sum_rmse, pack_sum_average_peak_score(self.plan_size)],
                     evals=[(dtrain, "tr")],
                     maximize=False,
                     verbose_eval=self.verbose_eval,
@@ -302,12 +299,7 @@ class XGBModel(PythonBasedModel):
                 breakdown = np.concatenate((breakdown, np.array(stage_score)))
         else:
             breakdown = np.concatenate(
-                (
-                    np.random.uniform(0, 1, (len(states),)),
-                    np.zeros(
-                        len(states),
-                    ),
-                )
+                (np.random.uniform(0, 1, (len(states),)), np.zeros(len(states)))
             )
 
         # Predict 0 for invalid states that failed to be lowered.
@@ -543,7 +535,7 @@ def pack_sum_average_peak_score(N):
             trial_scores = labels_group[trials]
             curve = max_curve(trial_scores) / np.max(labels_group)
             scores.append(np.mean(curve))
-        return "a-peak@%d" % N, np.mean(scores)
+        return f"a-peak@{N}", np.mean(scores)
 
     return feval
 
@@ -647,11 +639,11 @@ class CustomCallback(XGBoostCallback):
             and self.verbose_eval
             and epoch % self.verbose_eval == 0
         ):
-            infos = ["XGB iter: %3d" % epoch]
+            infos = [f"XGB iter: {epoch:3d}"]
             for item in eval_res:
                 if "null" in item[0]:
                     continue
-                infos.append("%s: %.6f" % (item[0], item[1]))
+                infos.append(f"{item[0]}: {item[1]:.6f}")
 
             logger.debug("\t".join(infos))
             if self.log_file:
@@ -671,7 +663,7 @@ class CustomCallback(XGBoostCallback):
         maximize_score = self.state["maximize_score"]
 
         if (maximize_score and score > best_score) or (not maximize_score and score < best_score):
-            msg = "[%d] %s" % (epoch, "\t".join([_fmt_metric(x) for x in eval_res]))
+            msg = f"[{epoch}] " + "\t".join([_fmt_metric(x) for x in eval_res])
             self.state["best_msg"] = msg
             self.state["best_score"] = score
             self.state["best_iteration"] = epoch
diff --git a/python/tvm/auto_scheduler/dispatcher.py b/python/tvm/auto_scheduler/dispatcher.py
index 98566f8636..3384850502 100644
--- a/python/tvm/auto_scheduler/dispatcher.py
+++ b/python/tvm/auto_scheduler/dispatcher.py
@@ -380,7 +380,7 @@ class ApplyHistoryBestOrSample(ApplyHistoryBest):
         task = SearchTask(workload_key=workload_key, target=target)
         measure_ctx = LocalRPCMeasureContext(min_repeat_ms=300)
 
-        log_file = self.log_dir.relpath("%s.log" % decode_workload_key(workload_key)[0])
+        log_file = self.log_dir.relpath(f"{decode_workload_key(workload_key)[0]}.log")
 
         while ret is None:
             tune_option = TuningOptions(
diff --git a/python/tvm/auto_scheduler/feature.py b/python/tvm/auto_scheduler/feature.py
index 21db37abd4..ea62560a6f 100644
--- a/python/tvm/auto_scheduler/feature.py
+++ b/python/tvm/auto_scheduler/feature.py
@@ -94,7 +94,7 @@ def unpack_feature(byte_arr: bytearray) -> Tuple[np.ndarray, np.ndarray, np.ndar
     n = struct.unpack_from("1i", byte_arr, offset=offset)[0]
     offset += SIZE_OF_INT32
 
-    sizes = struct.unpack_from("%di" % (n + 2), byte_arr, offset=offset)
+    sizes = struct.unpack_from(f"{n + 2}i", byte_arr, offset=offset)
     offset += SIZE_OF_INT32 * (n + 2)
 
     # unpack features
@@ -121,13 +121,10 @@ def unpack_feature(byte_arr: bytearray) -> Tuple[np.ndarray, np.ndarray, np.ndar
             tmp_vec_len = (size - 1) // n_stmts
             assert (
                 tmp_vec_len == vec_len
-            ), "The length of feature vector is wrong. Expected %d but got %d." % (
-                vec_len,
-                tmp_vec_len,
-            )
+            ), f"The length of feature vector is wrong. Expected {vec_len} but got {tmp_vec_len}."
             assert tmp_vec_len * n_stmts == size - 1
             for _ in range(n_stmts):
-                x = struct.unpack_from("%df" % vec_len, byte_arr, offset=offset)
+                x = struct.unpack_from(f"{vec_len}f", byte_arr, offset=offset)
                 offset += vec_len * SIZE_OF_FLOAT32
                 row.append(x)
 
@@ -135,15 +132,15 @@ def unpack_feature(byte_arr: bytearray) -> Tuple[np.ndarray, np.ndarray, np.ndar
 
     # unpack normalized_throughputs
     m = sizes[-2]
-    normalized_throughputs = struct.unpack_from("%df" % m, byte_arr, offset=offset)
+    normalized_throughputs = struct.unpack_from(f"{m}f", byte_arr, offset=offset)
     offset += m * SIZE_OF_FLOAT32
 
     # unpack task_ids
     m = sizes[-1]
-    task_ids = struct.unpack_from("%di" % m, byte_arr, offset=offset)
+    task_ids = struct.unpack_from(f"{m}i", byte_arr, offset=offset)
     offset += m * SIZE_OF_INT32
 
-    assert offset == len(byte_arr), "%d vs %d" % (offset, len(byte_arr))
+    assert offset == len(byte_arr), f"{offset} vs {len(byte_arr)}"
     return np.array(features, dtype=object), np.array(normalized_throughputs), np.array(task_ids)
 
 
diff --git a/python/tvm/auto_scheduler/measure.py b/python/tvm/auto_scheduler/measure.py
index e59e78f571..64da59031c 100644
--- a/python/tvm/auto_scheduler/measure.py
+++ b/python/tvm/auto_scheduler/measure.py
@@ -558,7 +558,7 @@ class LocalRPCMeasureContext:
         from tvm.rpc.tracker import Tracker
 
         self.tracker = Tracker(port=9000, port_end=10000, silent=True)
-        device_key = "$local$device$%d" % self.tracker.port
+        device_key = f"$local$device${self.tracker.port}"
         self.server = Server(
             port=self.tracker.port,
             port_end=10000,
@@ -698,15 +698,7 @@ def local_builder_build(inputs, timeout, n_parallel, build_func="default", verbo
         n_parallel, timeout, reset_global_scope, (AutotvmGlobalScope.current,)
     )
     tuple_res = executor.map_with_error_catching(
-        local_build_worker,
-        [
-            (
-                i.serialize(),
-                BuildFunc.build_func,
-                verbose,
-            )
-            for i in inputs
-        ],
+        local_build_worker, [(i.serialize(), BuildFunc.build_func, verbose) for i in inputs]
     )
 
     results = []
@@ -771,7 +763,7 @@ def register_task_input_check_func(func_name, f=None, override=False):
     def register(myf):
         """internal register function"""
         if func_name in TASK_INPUT_CHECK_FUNC_REGISTRY and not override:
-            raise RuntimeError("%s has been registered already" % func_name)
+            raise RuntimeError(f"{func_name} has been registered already")
         TASK_INPUT_CHECK_FUNC_REGISTRY[func_name] = myf
         return myf
 
@@ -869,8 +861,8 @@ def prepare_runner_args(inp, build_res):
                 task_inputs_count += 1
             else:
                 raise ValueError(
-                    "%s not found in task_inputs, " % (tensor_name)
-                    + "should provide with `SearchTask(..., task_inputs={...})`"
+                    f"{tensor_name} not found in task_inputs, "
+                    f"should provide with `SearchTask(..., task_inputs={{...}})`"
                 )
         else:
             args.append(None)
diff --git a/python/tvm/auto_scheduler/search_policy.py b/python/tvm/auto_scheduler/search_policy.py
index a88c1305b5..4b12c03183 100644
--- a/python/tvm/auto_scheduler/search_policy.py
+++ b/python/tvm/auto_scheduler/search_policy.py
@@ -237,7 +237,7 @@ class SketchPolicy(SearchPolicy):
         sketches = _ffi_api.SketchPolicyGenerateSketches(self)
         if print_for_debug:
             for i, s in enumerate(sketches):
-                print("=" * 20 + " %d " % i + "=" * 20)
+                print("=" * 20 + f" {i} " + "=" * 20)
                 print(s)
         return sketches
 
diff --git a/python/tvm/auto_scheduler/search_task.py b/python/tvm/auto_scheduler/search_task.py
index ab03ff9f8e..51a74dae93 100644
--- a/python/tvm/auto_scheduler/search_task.py
+++ b/python/tvm/auto_scheduler/search_task.py
@@ -238,9 +238,8 @@ def _save_buffer_to_file(buffer_name, buffer_data):
 
     buffer_name += "."
     for i in np_data.shape:
-        buffer_name += "%d_" % (i)
-    buffer_name += "%s" % (np_data.dtype)
-    buffer_name += ".npy"
+        buffer_name += f"{i}_"
+    buffer_name += f"{np_data.dtype}.npy"
 
     np_data.tofile(buffer_name, " ")
 
@@ -265,11 +264,7 @@ def _try_load_buffer_from_file(buffer_name):
 
 
 def register_task_input_buffer(
-    workload_key,
-    input_name,
-    input_data,
-    overwrite=False,
-    save_to_file=False,
+    workload_key, input_name, input_data, overwrite=False, save_to_file=False
 ):
     """Register special buffer for measurement.
 
@@ -360,8 +355,8 @@ def get_task_input_buffer(workload_key, input_name):
         return input_table[input_name]
 
     raise ValueError(
-        "%s not found in TASK_INPUT_BUFFER_TABLE, " % (input_name)
-        + "should provide with `SearchTask(..., task_inputs={...})`"
+        f"{input_name} not found in TASK_INPUT_BUFFER_TABLE, "
+        f"should provide with `SearchTask(..., task_inputs={{...}})`"
     )
 
 
@@ -519,7 +514,7 @@ class SearchTask(Object):
         )
         if inp is None:
             raise RuntimeError(
-                "Cannot find any valid schedule for %s in file %s" % (self.workload_key, log_file)
+                f"Cannot find any valid schedule for {self.workload_key} in file {log_file}"
             )
 
         sch, args = self.compute_dag.apply_steps_from_state(
@@ -546,7 +541,7 @@ class SearchTask(Object):
         inp, _ = load_best_record(log_file, self.workload_key)
         if inp is None:
             raise RuntimeError(
-                "Cannot find any valid schedule for %s in file %s" % (self.workload_key, log_file)
+                f"Cannot find any valid schedule for {self.workload_key} in file {log_file}"
             )
 
         if print_mode == "schedule":
@@ -556,7 +551,7 @@ class SearchTask(Object):
             sch, args = self.compute_dag.apply_steps_from_state(inp.state)
             func = build(sch, args, "cuda")
             return func.imported_modules[0].get_source()
-        raise ValueError("Invalid print_mode: %s" % print_mode)
+        raise ValueError(f"Invalid print_mode: {print_mode}")
 
     def __getstate__(self):
         self.target, self.target_host = Target.canon_target_and_host(self.target, self.target_host)
@@ -576,7 +571,7 @@ class SearchTask(Object):
         try:
             workload = json.loads(state["workload_key"])
         except Exception:  # pylint: disable=broad-except
-            raise RuntimeError("Invalid workload key %s" % state["workload_key"])
+            raise RuntimeError(f"Invalid workload key {state['workload_key']}")
 
         # workload[0] is either the compute function name or the ComputeDAG hash.
         # The compute functions are already registered when importing TVM, so here
diff --git a/python/tvm/auto_scheduler/task_scheduler.py b/python/tvm/auto_scheduler/task_scheduler.py
index c23c9b3c0c..547e5a5833 100644
--- a/python/tvm/auto_scheduler/task_scheduler.py
+++ b/python/tvm/auto_scheduler/task_scheduler.py
@@ -589,7 +589,7 @@ class PrintTableInfo(TaskSchedulerCallback):
 
         # content
         for i in range(len(task_scheduler.tasks)):
-            id_str = "%d" % i
+            id_str = f"{i}"
             latency_str = (
                 "%.3f" % (1e3 * task_scheduler.best_costs[i])
                 if task_scheduler.best_costs[i] < 1e9
@@ -619,12 +619,7 @@ class PrintTableInfo(TaskSchedulerCallback):
             total_latency_str = "-"
         print(
             "Estimated total latency: %s ms\tTrials: %d\tUsed time : %.0f s\tNext ID: %d\t"
-            % (
-                total_latency_str,
-                task_scheduler.ct,
-                time.time() - task_scheduler.tic,
-                task_id,
-            )
+            % (total_latency_str, task_scheduler.ct, time.time() - task_scheduler.tic, task_id)
         )
 
 
@@ -652,10 +647,6 @@ class LogEstimatedLatency(TaskSchedulerCallback):
         with open(self.log_file, "a") as filep:
             filep.write(
                 "ElapsedTime(s)\t%.0f\tEstimatedLatency(ms)\t%s\tTrials\t%d\n"
-                % (
-                    time.time() - task_scheduler.tic,
-                    total_latency_str,
-                    task_scheduler.ct,
-                )
+                % (time.time() - task_scheduler.tic, total_latency_str, task_scheduler.ct)
             )
             filep.flush()
diff --git a/python/tvm/auto_scheduler/utils.py b/python/tvm/auto_scheduler/utils.py
index 9919bcb470..4d05fc4856 100644
--- a/python/tvm/auto_scheduler/utils.py
+++ b/python/tvm/auto_scheduler/utils.py
@@ -359,9 +359,7 @@ def check_remote(device_key, host=None, port=None, priority=100, timeout=10):
     def _check():
         request_remote(device_key, host, port, priority)
 
-    t = threading.Thread(
-        target=_check,
-    )
+    t = threading.Thread(target=_check)
     t.start()
     t.join(timeout)
     return not t.is_alive()
@@ -407,6 +405,6 @@ def to_str_round(x, decimal=6):
     if isinstance(x, int):
         return str(x)
     if isinstance(x, (np.float32, np.float64, float)):
-        format_str = "%%.%df" % decimal
+        format_str = f"%.{decimal}f"
         return format_str % x
-    raise ValueError("Invalid value: " + str(x) + "\ttype: " + str(type(x)))
+    raise ValueError(f"Invalid value: {str(x)}\ttype: {type(x)}")
diff --git a/python/tvm/auto_scheduler/workload_registry.py b/python/tvm/auto_scheduler/workload_registry.py
index 17d2001e3a..117b6401b5 100644
--- a/python/tvm/auto_scheduler/workload_registry.py
+++ b/python/tvm/auto_scheduler/workload_registry.py
@@ -91,7 +91,7 @@ def register_workload(func_name, f=None, override=False):
     def register(myf):
         """internal register function"""
         if func_name in WORKLOAD_FUNC_REGISTRY and not override:
-            raise RuntimeError("%s has been registered already" % func_name)
+            raise RuntimeError(f"{func_name} has been registered already")
         WORKLOAD_FUNC_REGISTRY[func_name] = myf
         return myf
 
@@ -153,8 +153,8 @@ def make_workload_key(func, args):
 
     if not func_name in WORKLOAD_FUNC_REGISTRY:
         raise ValueError(
-            "%s is not registered. " % func,
-            "Please register it with @auto_scheduler.register_workload",
+            f"{func} is not registered. "
+            f"Please register it with @auto_scheduler.register_workload"
         )
 
     args = serialize_args(args)
diff --git a/python/tvm/autotvm/feature.py b/python/tvm/autotvm/feature.py
index f73c65fbd1..1b66d79d0e 100644
--- a/python/tvm/autotvm/feature.py
+++ b/python/tvm/autotvm/feature.py
@@ -136,7 +136,7 @@ def get_itervar_feature_flatten(sch, args, take_log=True):
     """
     stmt = ana_lower(sch, args, simple_mode=True)
     feas = _get_itervar_feature_flatten(stmt, take_log)
-    feas = struct.unpack("%df" % (len(feas) // 4), feas)
+    feas = struct.unpack(f"{len(feas) // 4}f", feas)
     return feas
 
 
@@ -154,8 +154,7 @@ def get_flatten_name(fea):
     """
 
     feature_name = {
-        "_attr_": ["length", "nest_level", "topdown", "bottomup"]
-        + ["ann_%d" % i for i in range(20)],
+        "_attr_": ["length", "nest_level", "topdown", "bottomup"] + [f"ann_{i}" for i in range(20)],
         "_arith_": ["add", "mul", "div"],
         "buf_touch": ["stride", "mod", "count", "reuse", "T_count", "T_reuse"],
     }
@@ -187,7 +186,7 @@ def get_flatten_name(fea):
                 name_list = feature_name["buf_touch"]
 
             for i in range(len((pair[1:]))):
-                names.append(".".join(["f%d" % ct, var_name, key, name_list[i]]))
+                names.append(".".join([f"f{ct}", var_name, key, name_list[i]]))
                 ct += 1
     return names
 
@@ -211,5 +210,5 @@ def get_buffer_curve_sample_flatten(sch, args, sample_n=30):
     """
     stmt = ana_lower(sch, args, simple_mode=True)
     feas = _get_buffer_curve_sample_flatten(stmt, sample_n, False)
-    feas = struct.unpack("%df" % (len(feas) // 4), feas)
+    feas = struct.unpack(f"{len(feas) // 4}f", feas)
     return feas
diff --git a/python/tvm/autotvm/graph_tuner/base_graph_tuner.py b/python/tvm/autotvm/graph_tuner/base_graph_tuner.py
index d4054bbd37..7e975201c8 100644
--- a/python/tvm/autotvm/graph_tuner/base_graph_tuner.py
+++ b/python/tvm/autotvm/graph_tuner/base_graph_tuner.py
@@ -49,7 +49,7 @@ def get_infer_layout(task_name):
         return topi.nn.conv2d_infer_layout
     if task_name.startswith("depthwise_conv2d"):
         return topi.nn.depthwise_conv2d_infer_layout
-    raise ValueError("Cannot find infer layout for task %s" % task_name)
+    raise ValueError(f"Cannot find infer layout for task {task_name}")
 
 
 @autotvm.template("layout_transform")
@@ -168,14 +168,14 @@ class BaseGraphTuner(object):
             graph = bind_inputs(graph, input_shapes, dtype)
             expr2graph(graph, self._target_ops, node_dict, self._node_list, target)
         else:
-            raise RuntimeError("Unsupported graph type: %s" % str(type(graph)))
+            raise RuntimeError(f"Unsupported graph type: {type(graph)}")
 
         self._graph = graph
         self._in_nodes_dict = get_in_nodes(self._node_list, self._target_ops, input_shapes.keys())
         if len(self._in_nodes_dict) == 0:
             raise RuntimeError(
-                "Could not find any input nodes with whose "
-                "operator is one of %s" % self._target_ops
+                f"Could not find any input nodes with whose "
+                f"operator is one of {self._target_ops}"
             )
         self._out_nodes_dict = get_out_nodes(self._in_nodes_dict)
         self._fetch_cfg()
@@ -583,7 +583,7 @@ class BaseGraphTuner(object):
             records = self.get_optimal_records()
             for record in records:
                 out_file.write(encode(record[0], record[1]) + "\n")
-        msg = "Writing optimal schedules to %s successfully." % record_file
+        msg = f"Writing optimal schedules to {record_file} successfully."
         self._logger.info(msg)
 
     @abstractmethod
diff --git a/python/tvm/autotvm/graph_tuner/pbqp_tuner.py b/python/tvm/autotvm/graph_tuner/pbqp_tuner.py
index 2cbd09fce8..c02cb2a5ad 100644
--- a/python/tvm/autotvm/graph_tuner/pbqp_tuner.py
+++ b/python/tvm/autotvm/graph_tuner/pbqp_tuner.py
@@ -172,7 +172,7 @@ class PBQPTuner(BaseGraphTuner):
 
         if record_idx < 0:
             raise RuntimeError(
-                "Can't find a soltuion for node %d when " "applying RN reduction" % node_idx
+                f"Can't find a soltuion for node {node_idx} when applying RN reduction"
             )
         self._optimal_record_dict[node_idx] = record_idx
         self._is_optimal = False
@@ -284,5 +284,5 @@ class PBQPTuner(BaseGraphTuner):
         self._forward()
         self._backward()
         is_optimal = "optimal" if self._is_optimal else "sub-optimal"
-        msg = "Finished PBQPExecutor run. Got %s solution." % is_optimal
+        msg = f"Finished PBQPExecutor run. Got {is_optimal} solution."
         self._logger.info(msg)
diff --git a/python/tvm/autotvm/graph_tuner/utils/traverse_graph.py b/python/tvm/autotvm/graph_tuner/utils/traverse_graph.py
index ed78cd689e..0c1ce36ba9 100644
--- a/python/tvm/autotvm/graph_tuner/utils/traverse_graph.py
+++ b/python/tvm/autotvm/graph_tuner/utils/traverse_graph.py
@@ -119,7 +119,7 @@ def _expr2graph_impl(expr, target_ops, node_dict, node_list, tvm_target):
                     node_entry["types"].append(tupe_type)
             else:
                 raise RuntimeError(
-                    "Unsupported output type %s in operator %s" % (type(out_type), op.name)
+                    f"Unsupported output type {type(out_type)} in operator {op.name}"
                 )
 
             # Utilize tracing target to fetch workload with topo-order.
@@ -138,7 +138,7 @@ def _expr2graph_impl(expr, target_ops, node_dict, node_list, tvm_target):
                             "find a target op %s with input type %s"
                             % (op, str(type(input_node_entry["node"])))
                         )
-                    free_var = relay.Var("var_%d" % i, input_type)
+                    free_var = relay.Var(f"var_{i}", input_type)
                     params.append(free_var)
                 call = relay.Call(node.op, params, node.attrs)
                 mod = tvm.IRModule.from_expr(relay.Function(params, call))
@@ -173,9 +173,7 @@ def _expr2graph_impl(expr, target_ops, node_dict, node_list, tvm_target):
         elif isinstance(node, tvm.ir.Op):
             return
         else:
-            raise RuntimeError(
-                "Not supported relay node type in graph tuning: %s" % str(type(node))
-            )
+            raise RuntimeError(f"Not supported relay node type in graph tuning: {type(node)}")
         node_dict[node] = node_index
         node_list.append(node_entry)
 
diff --git a/python/tvm/autotvm/measure/measure_methods.py b/python/tvm/autotvm/measure/measure_methods.py
index 5537d28c2d..eee15d8d87 100644
--- a/python/tvm/autotvm/measure/measure_methods.py
+++ b/python/tvm/autotvm/measure/measure_methods.py
@@ -149,10 +149,7 @@ class LocalBuilder(Builder):
                         # instantiation error
                         if isinstance(exception, InstantiationError):
                             res = MeasureResult(
-                                (
-                                    tb,
-                                    exception,
-                                ),
+                                (tb, exception),
                                 MeasureErrorNo.INSTANTIATION_ERROR,
                                 res.time_cost,
                                 time.time(),
@@ -166,10 +163,7 @@ class LocalBuilder(Builder):
                                 except Exception:  # pylint: disable=broad-except
                                     pass
                                 res = MeasureResult(
-                                    (
-                                        tb,
-                                        InstantiationError(msg),
-                                    ),
+                                    (tb, InstantiationError(msg)),
                                     MeasureErrorNo.INSTANTIATION_ERROR,
                                     res.time_cost,
                                     time.time(),
@@ -177,10 +171,7 @@ class LocalBuilder(Builder):
 
                             else:  # tvm error
                                 res = MeasureResult(
-                                    (
-                                        tb,
-                                        res.error,
-                                    ),
+                                    (tb, res.error),
                                     MeasureErrorNo.COMPILE_HOST,
                                     res.time_cost,
                                     time.time(),
@@ -188,24 +179,12 @@ class LocalBuilder(Builder):
                 except TimeoutError as ex:
                     tb = traceback.format_exc()
                     res = MeasureResult(
-                        (
-                            tb,
-                            ex,
-                        ),
-                        MeasureErrorNo.BUILD_TIMEOUT,
-                        self.timeout,
-                        time.time(),
+                        (tb, ex), MeasureErrorNo.BUILD_TIMEOUT, self.timeout, time.time()
                     )
                 except ChildProcessError as ex:
                     tb = traceback.format_exc()
                     res = MeasureResult(
-                        (
-                            tb,
-                            ex,
-                        ),
-                        MeasureErrorNo.RUNTIME_DEVICE,
-                        self.timeout,
-                        time.time(),
+                        (tb, ex), MeasureErrorNo.RUNTIME_DEVICE, self.timeout, time.time()
                     )
 
                 results.append(res)
@@ -395,13 +374,7 @@ class RPCRunner(Runner):
                     tb = traceback.format_exc()
                     results.append(
                         MeasureResult(
-                            (
-                                tb,
-                                ex,
-                            ),
-                            MeasureErrorNo.RUN_TIMEOUT,
-                            self.timeout,
-                            time.time(),
+                            (tb, ex), MeasureErrorNo.RUN_TIMEOUT, self.timeout, time.time()
                         )
                     )
 
@@ -479,7 +452,7 @@ class LocalRunner(RPCRunner):
 
         self.task = task
         tracker = Tracker(port=9000, port_end=10000, silent=True)
-        device_key = "$local$device$%d" % tracker.port
+        device_key = f"$local$device${tracker.port}"
         server = Server(
             port=9000,
             port_end=10000,
@@ -586,7 +559,7 @@ class _WrappedBuildFunc:
         tic = time.time()
         try:
             filename = os.path.join(
-                tmp_dir, "tmp_func_%0x.%s" % (getrandbits(64), self.build_func.output_format)
+                tmp_dir, f"tmp_func_{getrandbits(64):0x}.{self.build_func.output_format}"
             )
             # TODO(tvm-team) consider linline _build_func_common
             func, arg_info = _build_func_common(measure_input, self.runtime, **kwargs)
@@ -715,10 +688,7 @@ def run_through_rpc(
             msg = msg[: msg.index("Stack trace returned")]
         if "CUDA Source" in msg:
             msg = msg[: msg.index("CUDA Source")]
-        costs = (
-            traceback.format_exc(),
-            RuntimeError(msg[:1024]),
-        )
+        costs = (traceback.format_exc(), RuntimeError(msg[:1024]))
         errno = MeasureErrorNo.RUNTIME_DEVICE
     tstamp = time.time()
     time.sleep(cooldown_interval)
@@ -834,9 +804,7 @@ def check_remote(target, device_key, host=None, port=None, priority=100, timeout
             pass
         logger.debug("device available")
 
-    t = threading.Thread(
-        target=_check,
-    )
+    t = threading.Thread(target=_check)
     t.start()
     t.join(timeout)
 
diff --git a/python/tvm/autotvm/record.py b/python/tvm/autotvm/record.py
index 8e54e011c0..cde78d1dbc 100644
--- a/python/tvm/autotvm/record.py
+++ b/python/tvm/autotvm/record.py
@@ -279,13 +279,13 @@ def split_workload(in_file, clean=True):
 
             # write to file
             logger.info("Key: %s\tValid: %d\tDup: %d\t", k, len(cleaned), len(v) - len(cleaned))
-            with open(args.i + ".%03d.wkl" % i, "w") as fout:
+            with open(args.i + f".{i:03d}.wkl", "w") as fout:
                 for inp, res in cleaned:
                     fout.write(encode(inp, res) + "\n")
     else:
         for i, (k, v) in enumerate(wkl_dict.items()):
             logger.info("Key: %s\tNum: %d", k, len(v))
-            with open(args.i + ".%03d.wkl" % i, "w") as fout:
+            with open(args.i + f".{i:03d}.wkl", "w") as fout:
                 for inp, res in v:
                     fout.write(encode(inp, res) + "\n")
 
diff --git a/python/tvm/autotvm/task/dispatcher.py b/python/tvm/autotvm/task/dispatcher.py
index 8b2e7eb01f..f3d5c290f4 100644
--- a/python/tvm/autotvm/task/dispatcher.py
+++ b/python/tvm/autotvm/task/dispatcher.py
@@ -224,9 +224,7 @@ class ApplyFixedConfig(DispatchContext):
                 break
 
         if not config:
-            raise RuntimeError(
-                "workload: %s does not exist in %s" % (str(workload), str(self._tasks))
-            )
+            raise RuntimeError(f"workload: {str(workload)} does not exist in {str(self._tasks)}")
         # Add low cost to the target schedule and high cost to others.
         if workload[0] in self._schedule_names:
             config.cost = 1e-6
@@ -390,8 +388,8 @@ class FallbackContext(DispatchContext):
 
         if not _env.GLOBAL_SCOPE.silent:
             msg = (
-                "Cannot find config for target=%s, workload=%s. A fallback configuration "
-                "is used, which may bring great performance regression." % (target, workload)
+                f"Cannot find config for target={target}, workload={workload}. A fallback "
+                f"configuration is used, which may bring great performance regression."
             )
             if msg not in DispatchContext.warning_messages:
                 DispatchContext.warning_messages.add(msg)
@@ -510,9 +508,9 @@ class ApplyGraphBest(DispatchContext):
         key = (str(target), workload)
         if key not in self._global_cfg_dict:
             msg = (
-                "Config for target=%s, workload=%s is missing in ApplyGraphBest context. "
-                "A fallback configuration is used, which may bring great performance "
-                "regression." % (target, workload)
+                f"Config for target={target}, workload={workload} is missing in ApplyGraphBest "
+                f"context. A fallback configuration is used, which may bring great performance "
+                f"regression."
             )
             logger.warning(msg)
             cfg = FallbackConfigEntity()
diff --git a/python/tvm/autotvm/task/space.py b/python/tvm/autotvm/task/space.py
index dc41d19917..e81bad6946 100644
--- a/python/tvm/autotvm/task/space.py
+++ b/python/tvm/autotvm/task/space.py
@@ -124,7 +124,7 @@ class VirtualAxis(TransformSpace):
         self.num_output = 1
 
         if name is None:
-            name = "axis_%d" % VirtualAxis.name_ct
+            name = f"axis_{VirtualAxis.name_ct}"
             VirtualAxis.name_ct += 1
 
         self.name = name
@@ -146,7 +146,7 @@ class VirtualAxis(TransformSpace):
         return 1
 
     def __repr__(self):
-        return "vaxis(%s)" % self.name
+        return f"vaxis({self.name})"
 
 
 def get_factors(n):
@@ -224,7 +224,7 @@ class SplitSpace(TransformSpace):
                 # Include less, equal, and round-up power-of-two numbers. May generate tails.
                 factors = [x for x in get_pow2s(self.product) if x <= max_factor]
             else:
-                raise RuntimeError("Invalid policy: %s" % policy)
+                raise RuntimeError(f"Invalid policy: {policy}")
 
             # Enforce the product of all split factors equals to the axis length
             no_tail = kwargs.get("no_tail", policy == "factors")
@@ -361,7 +361,7 @@ class ReorderSpace(TransformSpace):
         return len(axes)
 
     def __repr__(self):
-        return "Reorder(policy=%s) len=%d" % (self.policy, len(self))
+        return f"Reorder(policy={self.policy}) len={len(self)}"
 
     def _merge_chain(self, chains):
         """generate all combinations of merge some chains"""
@@ -544,7 +544,7 @@ class AnnotateSpace(TransformSpace):
         return len(axes)
 
     def __repr__(self):
-        return "Annotate(policy=%s) len=%d" % (self.policy, len(self))
+        return f"Annotate(policy={self.policy}) len={len(self)}"
 
 
 class AnnotateEntity(object):
@@ -643,7 +643,7 @@ class OtherOptionSpace(TransformSpace):
         return 0
 
     def __repr__(self):
-        return "OtherOption(%s) len=%d" % (self.entities, len(self))
+        return f"OtherOption({self.entities}) len={len(self)}"
 
 
 class OtherOptionEntity(object):
@@ -1155,13 +1155,11 @@ class ConfigSpace(object):
             config corresponds to the index
         """
         if index < 0 or index >= self.range_length:
-            raise IndexError(
-                "Index out of range: size {}, got index {}".format(self.range_length, index)
-            )
+            raise IndexError(f"Index out of range: size {self.range_length}, got index {index}")
         if not self.is_index_valid(index):
             raise IndexError(
-                "Index does not correspond to the multi-filter condition, got index {}. "
-                "Use is_index_valid to pre-check".format(index)
+                f"Index does not correspond to the multi-filter condition, got index {index}. "
+                f"Use is_index_valid to pre-check"
             )
         entities = OrderedDict()
         t = index
@@ -1186,11 +1184,9 @@ class ConfigSpace(object):
         return self._entity_map[name]
 
     def __repr__(self):
-        res = "ConfigSpace (len={}, range_length={}, space_map=\n".format(
-            len(self), self.range_length
-        )
+        res = f"ConfigSpace (len={len(self)}, range_length={self.range_length}, space_map=\n"
         for i, (name, space) in enumerate(self.space_map.items()):
-            res += "  %2d %s: %s\n" % (i, name, space)
+            res += f"  {i:2d} {name}: {space}\n"
         return res + ")"
 
 
@@ -1331,7 +1327,7 @@ class ConfigEntity(ConfigSpace):
         return ConfigEntity(index, code_hash, entity_map, constraints)
 
     def __repr__(self):
-        return "%s,%s,%d" % (str(self._entity_map)[12:-1], self.code_hash, self.index)
+        return f"{str(self._entity_map)[12:-1]},{self.code_hash},{self.index}"
 
 
 class FallbackConfigEntity(ConfigSpace):
@@ -1445,4 +1441,4 @@ class FallbackConfigEntity(ConfigSpace):
         self._entity_map[name] = entity
 
     def __repr__(self):
-        return "%s,%s" % (str(self._entity_map)[12:-1], self.code_hash)
+        return f"{str(self._entity_map)[12:-1]},{self.code_hash}"
diff --git a/python/tvm/autotvm/task/task.py b/python/tvm/autotvm/task/task.py
index 18bc0720d5..575325c80e 100644
--- a/python/tvm/autotvm/task/task.py
+++ b/python/tvm/autotvm/task/task.py
@@ -70,8 +70,8 @@ def serialize_args(args):
         if x is None:
             return None
         raise RuntimeError(
-            'Do not support type "%s" in argument. Consider to use'
-            "primitive types or tvm.tir.Var only" % type(x)
+            f'Do not support type "{type(x)}" in argument. Consider to use'
+            f"primitive types or tvm.tir.Var only"
         )
 
     ret = []
@@ -304,7 +304,7 @@ def _register_task_compute(name, func=None):
             TASK_TABLE[name] = TaskTemplate()
         tmpl = TASK_TABLE[name]
         if tmpl.fcompute is not None:
-            raise ValueError("Compute is already registered in autoTVM task %s" % name)
+            raise ValueError(f"Compute is already registered in autoTVM task {name}")
         tmpl.fcompute = f
         return f
 
@@ -336,7 +336,7 @@ def _register_task_schedule(name, func=None):
             TASK_TABLE[name] = TaskTemplate()
         tmpl = TASK_TABLE[name]
         if tmpl.fschedule is not None:
-            raise ValueError("Schedule is already registered in autoTVM task %s" % name)
+            raise ValueError(f"Schedule is already registered in autoTVM task {name}")
         tmpl.fschedule = f
         return f
 
@@ -368,7 +368,7 @@ def _register_customized_task(name, func=None):
             TASK_TABLE[name] = TaskTemplate()
         tmpl = TASK_TABLE[name]
         if tmpl.fcustomized is not None:
-            raise ValueError("Customized func is already registered in autoTVM task %s" % name)
+            raise ValueError(f"Customized func is already registered in autoTVM task {name}")
         tmpl.fcustomized = f
         return f
 
diff --git a/python/tvm/autotvm/testing/tune_relay.py b/python/tvm/autotvm/testing/tune_relay.py
index 7de38e44c0..96e42fbea0 100644
--- a/python/tvm/autotvm/testing/tune_relay.py
+++ b/python/tvm/autotvm/testing/tune_relay.py
@@ -68,16 +68,10 @@ def _parse_args():
         help="The host address of the RPC tracker. Example: 192.168.6.66",
     )
     args.add_argument(
-        "--rpc-port",
-        type=int,
-        required=True,
-        help="The port of the RPC tracker. Example: 4445",
+        "--rpc-port", type=int, required=True, help="The port of the RPC tracker. Example: 4445"
     )
     args.add_argument(
-        "--rpc-key",
-        type=str,
-        required=True,
-        help="The key of the RPC tracker. Example: '3090ti'",
+        "--rpc-key", type=str, required=True, help="The key of the RPC tracker. Example: '3090ti'"
     )
     args.add_argument(
         "--work-dir",
@@ -91,26 +85,10 @@ def _parse_args():
         default=None,
         help="The layout of the workload. Example: 'NCHW', 'NHWC'",
     )
-    args.add_argument(
-        "--cache-dir",
-        type=str,
-        default=None,
-    )
-    args.add_argument(
-        "--number",
-        type=int,
-        default=3,
-    )
-    args.add_argument(
-        "--repeat",
-        type=int,
-        default=1,
-    )
-    args.add_argument(
-        "--min-repeat-ms",
-        type=int,
-        default=100,
-    )
+    args.add_argument("--cache-dir", type=str, default=None)
+    args.add_argument("--number", type=int, default=3)
+    args.add_argument("--repeat", type=int, default=1)
+    args.add_argument("--min-repeat-ms", type=int, default=100)
     args.add_argument(
         "--cpu-flush",
         type=lambda x: bool(strtobool(x)),
@@ -124,11 +102,7 @@ def _parse_args():
         required=True,
     )
     args.add_argument(
-        "--backend",
-        type=str,
-        choices=["graph", "vm"],
-        help="example: graph / vm",
-        required=True,
+        "--backend", type=str, choices=["graph", "vm"], help="example: graph / vm", required=True
     )
     parsed = args.parse_args()
     parsed.target = tvm.target.Target(parsed.target)
@@ -170,18 +144,9 @@ def main():
     describe()
     print(f"Workload: {ARGS.workload}")
     mod, params, (input_name, input_shape, input_dtype) = get_network(
-        ARGS.workload,
-        ARGS.input_shape,
-        layout=ARGS.layout,
-        cache_dir=ARGS.cache_dir,
+        ARGS.workload, ARGS.input_shape, layout=ARGS.layout, cache_dir=ARGS.cache_dir
     )
-    input_info = [
-        {
-            "name": input_name,
-            "shape": input_shape,
-            "dtype": input_dtype,
-        },
-    ]
+    input_info = [{"name": input_name, "shape": input_shape, "dtype": input_dtype}]
     input_data = {
         item["name"]: generate_input_data(item["shape"], item["dtype"]) for item in input_info
     }
@@ -211,7 +176,7 @@ def main():
         with ms.Profiler.timeit("Tuning"):
             if ARGS.num_trials > 0:
                 for i, task in enumerate(tasks):
-                    prefix = "[Task %2d/%2d] " % (i + 1, len(tasks))
+                    prefix = f"[Task {i + 1:2d}/{len(tasks):2d}] "
                     tuner_obj = XGBTuner(task, loss_type="reg")
                     n_trial = min(len(task.config_space), ARGS.num_trials)
                     tuner_obj.tune(
@@ -228,9 +193,7 @@ def main():
                         graph=mod["main"],
                         input_shapes={input_name: input_shape},
                         records=log_file,
-                        target_ops=[
-                            relay.op.get("nn.conv2d"),
-                        ],
+                        target_ops=[relay.op.get("nn.conv2d")],
                         target=ARGS.target,
                     )
                     executor.benchmark_layout_transform(min_exec_num=1000)
diff --git a/python/tvm/autotvm/tophub.py b/python/tvm/autotvm/tophub.py
index 1019f9ddff..3cbb7ff0e1 100644
--- a/python/tvm/autotvm/tophub.py
+++ b/python/tvm/autotvm/tophub.py
@@ -129,7 +129,7 @@ def context(target, extra_files=None):
                 if not check_backend(tophub_location, name):
                     continue
 
-                filename = "%s_%s.log" % (name, PACKAGE_VERSION[name])
+                filename = f"{name}_{PACKAGE_VERSION[name]}.log"
                 best_context.load(Path(AUTOTVM_TOPHUB_ROOT_PATH, filename))
                 break  # only load one file to avoid some fallback template mismatch problem
 
@@ -155,10 +155,10 @@ def check_backend(tophub_location, backend):
         Whether the check is successful.
     """
     backend = _alias(backend)
-    assert backend in PACKAGE_VERSION, 'Cannot find backend "%s" in TopHub' % backend
+    assert backend in PACKAGE_VERSION, f'Cannot find backend "{backend}" in TopHub'
 
     version = PACKAGE_VERSION[backend]
-    package_name = "%s_%s.log" % (backend, version)
+    package_name = f"{backend}_{version}.log"
     if Path(AUTOTVM_TOPHUB_ROOT_PATH, package_name).is_file():
         return True
 
@@ -189,7 +189,7 @@ def download_package(tophub_location, package_name):
     rootpath = Path(AUTOTVM_TOPHUB_ROOT_PATH)
     rootpath.mkdir(parents=True, exist_ok=True)
 
-    download_url = "{0}/{1}".format(tophub_location, package_name)
+    download_url = f"{tophub_location}/{package_name}"
     logger.info("Download pre-tuned parameters package from %s", download_url)
     download(download_url, Path(rootpath, package_name), overwrite=True)
 
@@ -216,7 +216,7 @@ def load_reference_log(backend, model, workload_name):
     if backend not in PACKAGE_VERSION:
         return []
     version = PACKAGE_VERSION[backend]
-    package_name = "%s_%s.log" % (backend, version)
+    package_name = f"{backend}_{version}.log"
     filename = Path(AUTOTVM_TOPHUB_ROOT_PATH, package_name)
 
     global REFERENCE_LOG_CACHE
diff --git a/python/tvm/autotvm/tuner/sa_model_optimizer.py b/python/tvm/autotvm/tuner/sa_model_optimizer.py
index a50f148f2e..518fc0e45e 100644
--- a/python/tvm/autotvm/tuner/sa_model_optimizer.py
+++ b/python/tvm/autotvm/tuner/sa_model_optimizer.py
@@ -130,7 +130,7 @@ class SimulatedAnnealingOptimizer(ModelOptimizer):
             t -= cool
 
             if log_interval and k % log_interval == 0:
-                t_str = "%.2f" % t
+                t_str = f"{t:.2f}"
                 logger.debug(
                     "SA iter: %d\tlast_update: %d\tmax-0: %.2f\tmax-1: %.2f\ttemp: %s\t"
                     "elapsed: %.2f",
diff --git a/python/tvm/autotvm/tuner/xgboost_cost_model.py b/python/tvm/autotvm/tuner/xgboost_cost_model.py
index d587625846..048eecf10e 100644
--- a/python/tvm/autotvm/tuner/xgboost_cost_model.py
+++ b/python/tvm/autotvm/tuner/xgboost_cost_model.py
@@ -220,12 +220,10 @@ class XGBoostCostModel(CostModel):
             callbacks=[
                 CustomCallback(
                     stopping_rounds=20,
-                    metric="tr-a-recall@%d" % plan_size,
+                    metric=f"tr-a-recall@{plan_size}",
                     evals=[(dtrain, "tr")],
                     maximize=True,
-                    fevals=[
-                        xgb_average_recalln_curve_score(plan_size),
-                    ],
+                    fevals=[xgb_average_recalln_curve_score(plan_size)],
                     verbose_eval=self.log_interval,
                     loss_type=self.loss_type,
                 )
@@ -305,12 +303,10 @@ class XGBoostCostModel(CostModel):
             callbacks=[
                 CustomCallback(
                     stopping_rounds=100,
-                    metric="tr-a-recall@%d" % plan_size,
+                    metric=f"tr-a-recall@{plan_size}",
                     evals=[(dtrain, "tr")],
                     maximize=True,
-                    fevals=[
-                        xgb_average_recalln_curve_score(plan_size),
-                    ],
+                    fevals=[xgb_average_recalln_curve_score(plan_size)],
                     verbose_eval=self.log_interval,
                     loss_type=self.loss_type,
                 )
@@ -591,11 +587,11 @@ class CustomCallback(XGBoostCallback):
             and self.verbose_eval
             and epoch % self.verbose_eval == 0
         ):
-            infos = ["XGB iter: %3d" % epoch]
+            infos = [f"XGB iter: {epoch:3d}"]
             for item in eval_res:
                 if "null" in item[0]:
                     continue
-                infos.append("%s: %.6f" % (item[0], item[1]))
+                infos.append(f"{item[0]}: {item[1]:.6f}")
 
             logger.debug("\t".join(infos))
             if self.log_file:
@@ -615,7 +611,7 @@ class CustomCallback(XGBoostCallback):
         maximize_score = self.state["maximize_score"]
 
         if (maximize_score and score > best_score) or (not maximize_score and score < best_score):
-            msg = "[%d] %s" % (epoch, "\t".join([_fmt_metric(x) for x in eval_res]))
+            msg = f"[{epoch}] " + "\t".join([_fmt_metric(x) for x in eval_res])
             self.state["best_msg"] = msg
             self.state["best_score"] = score
             self.state["best_iteration"] = epoch
@@ -644,7 +640,7 @@ def xgb_max_curve_score(N):
         trials = np.argsort(preds)[::-1]
         scores = labels[trials]
         curve = max_curve(scores)
-        return "Smax@%d" % N, curve[N] / np.max(labels)
+        return f"Smax@{N}", curve[N] / np.max(labels)
 
     return feval
 
@@ -657,7 +653,7 @@ def xgb_recalln_curve_score(N):
         trials = np.argsort(preds)[::-1]
         ranks = get_rank(labels[trials])
         curve = recall_curve(ranks)
-        return "recall@%d" % N, curve[N]
+        return f"recall@{N}", curve[N]
 
     return feval
 
@@ -670,7 +666,7 @@ def xgb_average_recalln_curve_score(N):
         trials = np.argsort(preds)[::-1]
         ranks = get_rank(labels[trials])
         curve = recall_curve(ranks)
-        return "a-recall@%d" % N, np.sum(curve[:N]) / N
+        return f"a-recall@{N}", np.sum(curve[:N]) / N
 
     return feval
 
@@ -683,7 +679,7 @@ def xgb_recallk_curve_score(N, topk):
         trials = np.argsort(preds)[::-1]
         ranks = get_rank(labels[trials])
         curve = recall_curve(ranks, topk)
-        return "recall@%d" % topk, curve[N]
+        return f"recall@{topk}", curve[N]
 
     return feval
 
@@ -696,7 +692,7 @@ def xgb_cover_curve_score(N):
         trials = np.argsort(preds)[::-1]
         ranks = get_rank(labels[trials])
         curve = cover_curve(ranks)
-        return "cover@%d" % N, curve[N]
+        return f"cover@{N}", curve[N]
 
     return feval